From 7fc064217bfdee0a064b00605543b6cbe6e16764 Mon Sep 17 00:00:00 2001 From: Ofer Levi <20116361+OferLevi85@users.noreply.github.com> Date: Thu, 8 Jul 2021 18:16:19 +0300 Subject: [PATCH] Remove grunt dependency (#1536) Signed-off-by: Ofer Levi --- clients/nodejs/zpe/.prettierrc | 10 + clients/nodejs/zpe/Gruntfile.js | 95 --- clients/nodejs/zpe/Makefile | 20 +- clients/nodejs/zpe/eslint.json | 14 - clients/nodejs/zpe/package.json | 19 +- clients/nodejs/zpe/src/AccessCheckStatus.js | 24 +- clients/nodejs/zpe/src/AuthZPEClient.js | 803 ++++++++++-------- clients/nodejs/zpe/src/PublicKeyStore.js | 83 +- clients/nodejs/zpe/src/ZPEMatch.js | 86 +- clients/nodejs/zpe/src/ZPEUpdater.js | 477 ++++++----- .../nodejs/zpe/test/config/IdentityMock.js | 50 +- clients/nodejs/zpe/test/config/KeyStore.js | 10 +- clients/nodejs/zpe/test/config/RdlMock.js | 18 +- .../nodejs/zpe/test/config/RoleTokenMock.js | 12 +- .../nodejs/zpe/test/config/SiaProviderMock.js | 28 +- clients/nodejs/zpe/test/config/helpers.js | 10 +- .../nodejs/zpe/test/unit/AccessCheckStatus.js | 35 +- clients/nodejs/zpe/test/unit/AuthZPEClient.js | 783 +++++++++-------- .../nodejs/zpe/test/unit/PublicKeyStore.js | 208 +++-- clients/nodejs/zpe/test/unit/ZPEMatch.js | 317 ++++--- clients/nodejs/zpe/test/unit/ZPEUpdater.js | 17 +- clients/nodejs/zpe/test/unit/index.js | 35 +- clients/nodejs/zts/.prettierrc | 10 + clients/nodejs/zts/Gruntfile.js | 95 --- clients/nodejs/zts/Makefile | 20 +- clients/nodejs/zts/eslint.json | 14 - clients/nodejs/zts/index.js | 509 ++++++----- clients/nodejs/zts/package.json | 19 +- .../nodejs/zts/test/config/IdentityMock.js | 50 +- clients/nodejs/zts/test/config/KeyStore.js | 10 +- clients/nodejs/zts/test/config/RdlMock.js | 18 +- .../nodejs/zts/test/config/RoleTokenMock.js | 12 +- .../nodejs/zts/test/config/SiaProviderMock.js | 28 +- clients/nodejs/zts/test/config/helpers.js | 10 +- clients/nodejs/zts/test/unit/index.js | 742 +++++++++------- libs/nodejs/auth_core/.prettierrc | 10 + libs/nodejs/auth_core/Gruntfile.js | 82 -- libs/nodejs/auth_core/Makefile | 20 +- libs/nodejs/auth_core/eslint.json | 14 - libs/nodejs/auth_core/index.js | 20 +- libs/nodejs/auth_core/package.json | 23 +- libs/nodejs/auth_core/src/impl/KeyStore.js | 18 +- .../auth_core/src/impl/PrincipalAuthority.js | 487 ++++++----- .../auth_core/src/impl/RoleAuthority.js | 215 +++-- .../auth_core/src/impl/SimplePrincipal.js | 452 +++++----- .../src/impl/SimpleServiceIdentityProvider.js | 146 ++-- .../auth_core/src/token/PrincipalToken.js | 721 +++++++++------- libs/nodejs/auth_core/src/token/RoleToken.js | 423 ++++----- libs/nodejs/auth_core/src/token/Token.js | 387 +++++---- libs/nodejs/auth_core/src/util/Crypto.js | 58 +- libs/nodejs/auth_core/src/util/Validate.js | 28 +- libs/nodejs/auth_core/src/util/YBase64.js | 28 +- libs/nodejs/auth_core/test/config/KeyStore.js | 10 +- .../test/config/PrincipalAuthorityMock.js | 12 +- .../test/config/SimplePrincipalMock.js | 18 +- libs/nodejs/auth_core/test/config/helpers.js | 10 +- package-lock.json | 3 + 57 files changed, 4178 insertions(+), 3698 deletions(-) create mode 100644 clients/nodejs/zpe/.prettierrc delete mode 100644 clients/nodejs/zpe/Gruntfile.js delete mode 100644 clients/nodejs/zpe/eslint.json create mode 100644 clients/nodejs/zts/.prettierrc delete mode 100644 clients/nodejs/zts/Gruntfile.js delete mode 100644 clients/nodejs/zts/eslint.json create mode 100644 libs/nodejs/auth_core/.prettierrc delete mode 100644 libs/nodejs/auth_core/Gruntfile.js delete mode 100644 libs/nodejs/auth_core/eslint.json create mode 100644 package-lock.json diff --git a/clients/nodejs/zpe/.prettierrc b/clients/nodejs/zpe/.prettierrc new file mode 100644 index 00000000000..6bb22e39328 --- /dev/null +++ b/clients/nodejs/zpe/.prettierrc @@ -0,0 +1,10 @@ +arrowParens: always +bracketSpacing: true +endOfLine: lf +jsxBracketSameLine: false +jsxSingleQuote: true +semi: true +singleQuote: true +tabWidth: 4 +trailingComma: es5 +useTabs: false diff --git a/clients/nodejs/zpe/Gruntfile.js b/clients/nodejs/zpe/Gruntfile.js deleted file mode 100644 index 51e37542589..00000000000 --- a/clients/nodejs/zpe/Gruntfile.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*jshint camelcase: false*/ -'use strict'; - -//var config = require('./config/config.js')(); - -module.exports = function(grunt) { - require('load-grunt-tasks')(grunt); - var path = require('path'), - pretty = require('prettysize'); - - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - copy: {}, - clean: ['build/', 'artifacts/'], - browserify: {}, - eslint: { - options: { - cache: true, - cacheFile: '.eslintcodecache', - configFile: 'eslint.json' - }, - target: ['src/**/*.js', './*.js', 'test/**/*.js'] - }, - filesize: {}, - jshint: { - options: { - globalstrict: true, - expr: true, - esversion: 6, - globals: { - "expect": true, - "assert": false, - "it": false, - "require": false, - "describe": false, - "beforeEach": false, - "afterEach": false, - "before": false, - "after": false, - "$": false, - "$$": false, - "browser": false, - "Buffer": false, - "module": false, - "global": false, - "exports": false, - "process": false, - "console": false, - "__dirname": false, - "Intl": false, - } - }, - files: ['Gruntfile.js', 'src/**/*.js', './*.js', 'test/**/*.js'], - } - }); - - grunt.registerTask('test', 'Run tests', function(){ - grunt.log.ok('Running unit tests with node@' + process.version); - var done = this.async(); - grunt.util.spawn({ - cmd: path.join(__dirname, 'node_modules/nyc/bin/nyc.js'), - args: ['node_modules/.bin/jenkins-mocha', '--require', 'test/config/mock.js', 'test/unit/**/*.js'], - opts: { stdio: 'inherit' } - }, done); - }); - - grunt.registerTask('lint', function() { - grunt.task.run(['jshint', 'eslint']); - }); - - grunt.registerTask('build', function() { - grunt.task.run(['clean']); - }); - - grunt.registerTask('build-dev', function() { - grunt.task.run(['clean']); - }); - - grunt.registerTask('default', ['lint', 'build', 'test']); - - grunt.registerTask('local', ['build-dev']); -}; diff --git a/clients/nodejs/zpe/Makefile b/clients/nodejs/zpe/Makefile index 4b340685bf2..49252bd56d5 100644 --- a/clients/nodejs/zpe/Makefile +++ b/clients/nodejs/zpe/Makefile @@ -10,10 +10,8 @@ NPM := $(shell command -v npm 2> /dev/null) ifdef NPM -all: - npm install - $(PWD)/node_modules/grunt-cli/bin/grunt - +.PHONY: build lint test +all: build lint test else all: @@ -21,5 +19,17 @@ all: endif +build: + @echo "Make: Build" + npm install + +lint: + @echo "Make: lint" + npm run ci-lint + +test: + @echo "Make: unit tests" + npm run test + clean: - rm -rf build node_modules artifacts + rm -rf node_modules artifacts diff --git a/clients/nodejs/zpe/eslint.json b/clients/nodejs/zpe/eslint.json deleted file mode 100644 index b4c9af40abc..00000000000 --- a/clients/nodejs/zpe/eslint.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "env": { - "es6": true - }, - "rules": { - "eqeqeq": "warn", - "curly": "warn", - "no-undef": "off", - "no-undefined": "off", - "no-unused-vars": "off", - "max-params": ["error", {"max":5}], - "complexity": ["error", {"max":8}] - } -} diff --git a/clients/nodejs/zpe/package.json b/clients/nodejs/zpe/package.json index a04897f8c81..f3353b3455f 100644 --- a/clients/nodejs/zpe/package.json +++ b/clients/nodejs/zpe/package.json @@ -11,10 +11,10 @@ }, "scripts": { "build": "make", - "lint": "grunt lint", + "fix-lint": "prettier --write src/**/*.js ./*.js test/**/*.js", + "ci-lint": "prettier --list-different src/**/*.js ./*.js test/**/*.js", "test": "./node_modules/nyc/bin/nyc.js node_modules/.bin/jenkins-mocha --require test/config/mock.js test/unit/*.js", "testDev": "mocha --require test/config/mock.js test/unit/*.js", - "functional": "grunt functional-sd", "noop": "echo Ignoring this section." }, "keywords": [ @@ -36,28 +36,15 @@ "@athenz/auth-core": "^1.0.0" }, "devDependencies": { + "prettier": "^2.3.0", "chai": "~3.5.0", "chai-as-promised": "~6.0.0", "chart.js": "^2.2.1", "csslint": "^1.0.5", - "grunt": "~0.4.5", - "grunt-browserify": "~5.0.0", - "grunt-cli": "~1.2.0", - "grunt-contrib-clean": "~0.7.0", - "grunt-contrib-copy": "~1.0.0", - "grunt-contrib-jshint": "^1.1.0", - "grunt-contrib-watch": "^1.0.0", - "grunt-eslint": "^19.0.0", - "grunt-exec": "~1.0.1", - "grunt-filesize": "0.0.7", - "grunt-postcss": "^0.8.0", - "grunt-protractor-webdriver": "~0.2.5", - "grunt-webdriver": "~1.0.0", "jenkins-mocha": "^5.0.0", "jquery": "^3.1.1", "jquery-ui": "^1.10.5", "jshint": "^2.9.4", - "load-grunt-tasks": "^3.5.2", "minifyify": "~7.3.5", "mkdirp": "~0.5.1", "mocha": "~3.2.0", diff --git a/clients/nodejs/zpe/src/AccessCheckStatus.js b/clients/nodejs/zpe/src/AccessCheckStatus.js index 810a3db738f..607c6a9d5b0 100644 --- a/clients/nodejs/zpe/src/AccessCheckStatus.js +++ b/clients/nodejs/zpe/src/AccessCheckStatus.js @@ -1,14 +1,18 @@ 'use strict'; module.exports = { - ALLOW: "Access Check was explicitly allowed", - DENY: "Access Check was explicitly denied", - DENY_NO_MATCH: "Access denied due to no match to any of the assertions defined in domain policy file", - DENY_ROLETOKEN_EXPIRED: "Access denied due to expired RoleToken", - DENY_ROLETOKEN_INVALID: "Access denied due to invalid RoleToken", - DENY_DOMAIN_MISMATCH: "Access denied due to domain mismatch between Resource and RoleToken", - DENY_DOMAIN_NOT_FOUND: "Access denied due to domain not found in library cache", - DENY_DOMAIN_EXPIRED: "Access denied due to expired domain policy file", - DENY_DOMAIN_EMPTY: "Access denied due to no policies in the domain file", - DENY_INVALID_PARAMETERS: "Access denied due to invalid/empty action/resource values", + ALLOW: 'Access Check was explicitly allowed', + DENY: 'Access Check was explicitly denied', + DENY_NO_MATCH: + 'Access denied due to no match to any of the assertions defined in domain policy file', + DENY_ROLETOKEN_EXPIRED: 'Access denied due to expired RoleToken', + DENY_ROLETOKEN_INVALID: 'Access denied due to invalid RoleToken', + DENY_DOMAIN_MISMATCH: + 'Access denied due to domain mismatch between Resource and RoleToken', + DENY_DOMAIN_NOT_FOUND: + 'Access denied due to domain not found in library cache', + DENY_DOMAIN_EXPIRED: 'Access denied due to expired domain policy file', + DENY_DOMAIN_EMPTY: 'Access denied due to no policies in the domain file', + DENY_INVALID_PARAMETERS: + 'Access denied due to invalid/empty action/resource values', }; diff --git a/clients/nodejs/zpe/src/AuthZPEClient.js b/clients/nodejs/zpe/src/AuthZPEClient.js index 7794fc6ed06..054acf00bff 100644 --- a/clients/nodejs/zpe/src/AuthZPEClient.js +++ b/clients/nodejs/zpe/src/AuthZPEClient.js @@ -7,394 +7,533 @@ const RoleToken = require('@athenz/auth-core').RoleToken; const AccessCheckStatus = require('./AccessCheckStatus'); const PublicKeyStore = require('./PublicKeyStore'); -let _publicKeyStore, - _allowedOffset; +let _publicKeyStore, _allowedOffset; let initialized = false; let ZPEUpdater; class AuthZPEClient { - - static init() { - if (!initialized) { - winston.level = config.logLevel; - - _publicKeyStore = new PublicKeyStore(); - _allowedOffset = Number(config.allowedOffset); - if (_allowedOffset < 0) { - _allowedOffset = 300; - } - - /* - * Since ZPEUpdater is still undefined while setConfig is called, - * we are going to set configuration settings for ZPEUpdater before AuthZPEClient is initialized. - */ - - ZPEUpdater = require(config.updater); - ZPEUpdater.setConfig({ - zpeClient: config - }); - ZPEUpdater.setZPEClient(AuthZPEClient); - if (config.disableWatch === false) { - ZPEUpdater.watchPolicyDir(); - } - - initialized = true; - } - } - - static setConfig(c) { - config = Object.assign({}, config, c.zpeClient); - PublicKeyStore.setConfig(c); - } - - static getZtsPublicKey(keyId) { - this.init(); - return _publicKeyStore.getZtsKey(keyId); - } - - static getZmsPublicKey(keyId) { - this.init(); - return _publicKeyStore.getZmsKey(keyId); - } - - static allowAccess(params, cb) { - this.init(); - let roleToken = params.roleToken, - resource = params.resource, - action = params.action; - - if (!roleToken || !resource || !action) { - return cb('ERROR: paramater must include 3 members: roleToken, resource, action', - AccessCheckStatus.DENY_INVALID_PARAMETERS); + static init() { + if (!initialized) { + winston.level = config.logLevel; + + _publicKeyStore = new PublicKeyStore(); + _allowedOffset = Number(config.allowedOffset); + if (_allowedOffset < 0) { + _allowedOffset = 300; + } + + /* + * Since ZPEUpdater is still undefined while setConfig is called, + * we are going to set configuration settings for ZPEUpdater before AuthZPEClient is initialized. + */ + + ZPEUpdater = require(config.updater); + ZPEUpdater.setConfig({ + zpeClient: config, + }); + ZPEUpdater.setZPEClient(AuthZPEClient); + if (config.disableWatch === false) { + ZPEUpdater.watchPolicyDir(); + } + + initialized = true; + } } - winston.debug("allowAccess: action=" + action + " resource=" + resource); + static setConfig(c) { + config = Object.assign({}, config, c.zpeClient); + PublicKeyStore.setConfig(c); + } - let tokenCache = ZPEUpdater.getRoleTokenCacheMap(); - let rToken = tokenCache.get(roleToken); + static getZtsPublicKey(keyId) { + this.init(); + return _publicKeyStore.getZtsKey(keyId); + } - if (!rToken) { - winston.debug("allowAccess: Role Token Cache Miss"); + static getZmsPublicKey(keyId) { + this.init(); + return _publicKeyStore.getZmsKey(keyId); + } - rToken = new RoleToken(roleToken); + static allowAccess(params, cb) { + this.init(); + let roleToken = params.roleToken, + resource = params.resource, + action = params.action; + + if (!roleToken || !resource || !action) { + return cb( + 'ERROR: paramater must include 3 members: roleToken, resource, action', + AccessCheckStatus.DENY_INVALID_PARAMETERS + ); + } - // validate the token - if (rToken.validate(this.getZtsPublicKey(rToken.getKeyId()), _allowedOffset, true) === false) { + winston.debug( + 'allowAccess: action=' + action + ' resource=' + resource + ); + + let tokenCache = ZPEUpdater.getRoleTokenCacheMap(); + let rToken = tokenCache.get(roleToken); + + if (!rToken) { + winston.debug('allowAccess: Role Token Cache Miss'); + + rToken = new RoleToken(roleToken); + + // validate the token + if ( + rToken.validate( + this.getZtsPublicKey(rToken.getKeyId()), + _allowedOffset, + true + ) === false + ) { + // check the token expiration + const now = Date.now() / 1000; + const expiry = Number(rToken.getExpiryTime()); + if (expiry !== 0 && expiry < now) { + const signedToken = rToken.getSignedToken(); + winston.error( + 'allowAccess: Authorization denied. Token expired. now=' + + now + + ' expiry=' + + expiry + + ' token=' + + signedToken + ); + + const tokenCache = ZPEUpdater.getRoleTokenCacheMap(); + tokenCache.del(signedToken); + + return cb( + 'ERROR: Role Token is Expired', + AccessCheckStatus.DENY_ROLETOKEN_EXPIRED + ); + } + + winston.error( + 'allowAccess: Authorization denied. Authentication of token failed for token=' + + rToken.getSignedToken() + ); + return cb( + 'ERROR: Invalid Role Token', + AccessCheckStatus.DENY_ROLETOKEN_INVALID + ); + } + + if (tokenCache) { + tokenCache.put(roleToken, rToken, config.tokenRefresh * 1000); + } + } else { + winston.debug('allowAccess: Role Token Cache Hit'); + } - // check the token expiration - const now = Date.now() / 1000; - const expiry = Number(rToken.getExpiryTime()); - if (expiry !== 0 && expiry < now) { - const signedToken = rToken.getSignedToken(); - winston.error("allowAccess: Authorization denied. Token expired. now=" + - now + " expiry=" + expiry + " token=" + signedToken); + delete params.roleToken; + params.rToken = rToken; - const tokenCache = ZPEUpdater.getRoleTokenCacheMap(); - tokenCache.del(signedToken); + return this._allowAccessByTokenObj(params, cb); + } - return cb('ERROR: Role Token is Expired', AccessCheckStatus.DENY_ROLETOKEN_EXPIRED); + static close() { + if (config.disableWatch === false) { + ZPEUpdater.closeWatcher(); } - - winston.error("allowAccess: Authorization denied. Authentication of token failed for token=" + - rToken.getSignedToken()); - return cb('ERROR: Invalid Role Token', AccessCheckStatus.DENY_ROLETOKEN_INVALID); - } - - if (tokenCache) { - tokenCache.put(roleToken, rToken, config.tokenRefresh * 1000); - } - } else { - winston.debug("allowAccess: Role Token Cache Hit"); + ZPEUpdater.close(); } - delete params.roleToken; - params.rToken = rToken; + static _allowAccessByTokenObj(params, cb) { + let rToken = params.rToken, + resource = params.resource, + action = params.action; + if (!rToken) { + winston.error('allowAccess: Authorization denied. Token is null'); + return cb(null, AccessCheckStatus.DENY_ROLETOKEN_INVALID); + } - return this._allowAccessByTokenObj(params, cb); - } + delete params.rToken; + params.domain = rToken.getDomain(); + params.roles = rToken.getRoles(); - static close() { - if (config.disableWatch === false) { - ZPEUpdater.closeWatcher(); + return this._allowActionZPE(params, cb); } - ZPEUpdater.close(); - } - - static _allowAccessByTokenObj(params, cb) { - let rToken = params.rToken, - resource = params.resource, - action = params.action; - if (!rToken) { - winston.error("allowAccess: Authorization denied. Token is null"); - return cb(null, AccessCheckStatus.DENY_ROLETOKEN_INVALID); - } - - delete params.rToken; - params.domain = rToken.getDomain(); - params.roles = rToken.getRoles(); - return this._allowActionZPE(params, cb); - } + static _allowActionZPE(params, cb) { + let action = params.action, + domain = params.domain, + resource = params.resource, + roles = params.roles; + let msgPrefix = + 'allowActionZPE: domain(' + domain + ') resource(' + resource + ')'; + + if (!roles || roles.length === 0) { + return cb( + msgPrefix + ' ERROR: No roles so access denied', + AccessCheckStatus.DENY_ROLETOKEN_INVALID + ); + } - static _allowActionZPE(params, cb) { - let action = params.action, - domain = params.domain, - resource = params.resource, - roles = params.roles; - let msgPrefix = "allowActionZPE: domain(" + domain + - ") resource(" + resource + ")"; + if (!domain) { + return cb( + msgPrefix + ' ERROR: No domain so access denied', + AccessCheckStatus.DENY_ROLETOKEN_INVALID + ); + } - if (!roles || roles.length === 0) { - return cb(msgPrefix + ' ERROR: No roles so access denied', AccessCheckStatus.DENY_ROLETOKEN_INVALID); - } + if (!action) { + return cb( + msgPrefix + ' ERROR: No action so access denied', + AccessCheckStatus.DENY_INVALID_PARAMETERS + ); + } - if (!domain) { - return cb(msgPrefix + ' ERROR: No domain so access denied', AccessCheckStatus.DENY_ROLETOKEN_INVALID); - } + if (!resource) { + return cb( + msgPrefix + ' ERROR: No resource so access denied', + AccessCheckStatus.DENY_INVALID_PARAMETERS + ); + } - if (!action) { - return cb(msgPrefix + ' ERROR: No action so access denied', AccessCheckStatus.DENY_INVALID_PARAMETERS); - } + params.msgPrefix = msgPrefix; - if (!resource) { - return cb(msgPrefix + ' ERROR: No resource so access denied', AccessCheckStatus.DENY_INVALID_PARAMETERS); + return this._allowActionZPEChecked(params, cb); } - params.msgPrefix = msgPrefix; + static _allowActionZPEChecked(params, cb) { + let action = params.action, + domain = params.domain, + resource = params.resource, + roles = params.roles, + msgPrefix = params.msgPrefix; + params.action = action.toLowerCase(); + params.resource = this.stripDomainPrefix( + resource.toLowerCase(), + domain, + null + ); + + // Note: if domain in token doesn't match domain in resource then there + // will be no match of any resource in the assertions - so deny immediately + + if (!params.resource) { + let msg = + msgPrefix + + ' ERROR: Domain mismatch in token(' + + domain + + ') and resource so access denied'; + return cb(msg, AccessCheckStatus.DENY_DOMAIN_MISMATCH); + } - return this._allowActionZPEChecked(params, cb); - } + params.status = AccessCheckStatus.DENY_DOMAIN_NOT_FOUND; - static _allowActionZPEChecked(params, cb) { - let action = params.action, - domain = params.domain, - resource = params.resource, - roles = params.roles, - msgPrefix = params.msgPrefix; - params.action = action.toLowerCase(); - params.resource = this.stripDomainPrefix(resource.toLowerCase(), domain, null); - - // Note: if domain in token doesn't match domain in resource then there - // will be no match of any resource in the assertions - so deny immediately - - if (!params.resource) { - let msg = msgPrefix + ' ERROR: Domain mismatch in token(' + - domain + ') and resource so access denied'; - return cb(msg, AccessCheckStatus.DENY_DOMAIN_MISMATCH); + return this._checkDenyAssertion(params, cb); } - params.status = AccessCheckStatus.DENY_DOMAIN_NOT_FOUND; - - return this._checkDenyAssertion(params, cb); - } - - static _checkDenyAssertion(params, cb) { - let action = params.action, - domain = params.domain, - resource = params.resource, - roles = params.roles, - status = params.status, - msgPrefix = params.msgPrefix; - // first hunt by role for deny assertions since deny takes precedence - // over allow assertions - - let roleMap = ZPEUpdater.getRoleDenyAssertions(domain); - if (roleMap && Object.keys(roleMap).length > 0) { - if (this._actionByRole(action, domain, resource, roles, roleMap)) { - return cb(null, AccessCheckStatus.DENY); - } else { - status = AccessCheckStatus.DENY_NO_MATCH; - } - } else if (roleMap) { - status = AccessCheckStatus.DENY_DOMAIN_EMPTY; - } + static _checkDenyAssertion(params, cb) { + let action = params.action, + domain = params.domain, + resource = params.resource, + roles = params.roles, + status = params.status, + msgPrefix = params.msgPrefix; + // first hunt by role for deny assertions since deny takes precedence + // over allow assertions + + let roleMap = ZPEUpdater.getRoleDenyAssertions(domain); + if (roleMap && Object.keys(roleMap).length > 0) { + if (this._actionByRole(action, domain, resource, roles, roleMap)) { + return cb(null, AccessCheckStatus.DENY); + } else { + status = AccessCheckStatus.DENY_NO_MATCH; + } + } else if (roleMap) { + status = AccessCheckStatus.DENY_DOMAIN_EMPTY; + } - // if the check was not explicitly denied by a standard role, then - // let's process our wildcard roles for deny assertions - - roleMap = ZPEUpdater.getWildcardDenyAssertions(domain); - if (roleMap && Object.keys(roleMap).length > 0) { - if (this._actionByWildcardRole(action, domain, resource, roles, roleMap)) { - return cb(null, AccessCheckStatus.DENY); - } else { - status = AccessCheckStatus.DENY_NO_MATCH; - } - } else if (status !== AccessCheckStatus.DENY_NO_MATCH && roleMap) { - status = AccessCheckStatus.DENY_DOMAIN_EMPTY; - } + // if the check was not explicitly denied by a standard role, then + // let's process our wildcard roles for deny assertions + + roleMap = ZPEUpdater.getWildcardDenyAssertions(domain); + if (roleMap && Object.keys(roleMap).length > 0) { + if ( + this._actionByWildcardRole( + action, + domain, + resource, + roles, + roleMap + ) + ) { + return cb(null, AccessCheckStatus.DENY); + } else { + status = AccessCheckStatus.DENY_NO_MATCH; + } + } else if (status !== AccessCheckStatus.DENY_NO_MATCH && roleMap) { + status = AccessCheckStatus.DENY_DOMAIN_EMPTY; + } - params.status = status; - - return this._checkAllowAssertion(params, cb); - } - - static _checkAllowAssertion(params, cb) { - let action = params.action, - domain = params.domain, - resource = params.resource, - roles = params.roles, - status = params.status, - msgPrefix = params.msgPrefix; - - // so far it did not match any deny assertions so now let's - // process our allow assertions - - let roleMap = ZPEUpdater.getRoleAllowAssertions(domain); - if (roleMap && Object.keys(roleMap).length > 0) { - if (this._actionByRole(action, domain, resource, roles, roleMap)) { - return cb(null, AccessCheckStatus.ALLOW); - } else { - status = AccessCheckStatus.DENY_NO_MATCH; - } - } else if (status !== AccessCheckStatus.DENY_NO_MATCH && roleMap) { - status = AccessCheckStatus.DENY_DOMAIN_EMPTY; - } + params.status = status; - // at this point we either got an allow or didn't match anything so we're - // going to try the wildcard roles - - roleMap = ZPEUpdater.getWildcardAllowAssertions(domain); - if (roleMap && Object.keys(roleMap).length > 0) { - if (this._actionByWildcardRole(action, domain, resource, roles, roleMap)) { - return cb(null, AccessCheckStatus.ALLOW); - } else { - status = AccessCheckStatus.DENY_NO_MATCH; - } - } else if (status !== AccessCheckStatus.DENY_NO_MATCH && roleMap) { - status = AccessCheckStatus.DENY_DOMAIN_EMPTY; + return this._checkAllowAssertion(params, cb); } - params.status = status; - - return this._checkStatus(params, cb); - } - - static _checkStatus(params, cb) { - let action = params.action, - domain = params.domain, - resource = params.resource, - roles = params.roles, - status = params.status, - msgPrefix = params.msgPrefix; - - if (status === AccessCheckStatus.DENY_DOMAIN_NOT_FOUND) { - winston.debug(msgPrefix + ": No role map found for domain=" + domain + - " so access denied"); - } else if (status === AccessCheckStatus.DENY_DOMAIN_EMPTY) { - winston.debug(msgPrefix + ": No policy assertions for domain=" + domain + - " so access denied"); - } + static _checkAllowAssertion(params, cb) { + let action = params.action, + domain = params.domain, + resource = params.resource, + roles = params.roles, + status = params.status, + msgPrefix = params.msgPrefix; + + // so far it did not match any deny assertions so now let's + // process our allow assertions + + let roleMap = ZPEUpdater.getRoleAllowAssertions(domain); + if (roleMap && Object.keys(roleMap).length > 0) { + if (this._actionByRole(action, domain, resource, roles, roleMap)) { + return cb(null, AccessCheckStatus.ALLOW); + } else { + status = AccessCheckStatus.DENY_NO_MATCH; + } + } else if (status !== AccessCheckStatus.DENY_NO_MATCH && roleMap) { + status = AccessCheckStatus.DENY_DOMAIN_EMPTY; + } - return cb(null, status); - } + // at this point we either got an allow or didn't match anything so we're + // going to try the wildcard roles + + roleMap = ZPEUpdater.getWildcardAllowAssertions(domain); + if (roleMap && Object.keys(roleMap).length > 0) { + if ( + this._actionByWildcardRole( + action, + domain, + resource, + roles, + roleMap + ) + ) { + return cb(null, AccessCheckStatus.ALLOW); + } else { + status = AccessCheckStatus.DENY_NO_MATCH; + } + } else if (status !== AccessCheckStatus.DENY_NO_MATCH && roleMap) { + status = AccessCheckStatus.DENY_DOMAIN_EMPTY; + } - static stripDomainPrefix(assertString, domain, defaultValue) { - this.init(); - let index = assertString.indexOf(':'); - if (index === -1) { - return assertString; - } + params.status = status; - if (assertString.substring(0, index) !== domain) { - return defaultValue; + return this._checkStatus(params, cb); } - return assertString.substring(index + 1); - } - - static _actionByRole(action, domain, resource, roles, roleMap) { - let msgPrefix = "allowActionByRole: domain(" + domain + - ") action(" + action + ") resource(" + resource +")"; + static _checkStatus(params, cb) { + let action = params.action, + domain = params.domain, + resource = params.resource, + roles = params.roles, + status = params.status, + msgPrefix = params.msgPrefix; + + if (status === AccessCheckStatus.DENY_DOMAIN_NOT_FOUND) { + winston.debug( + msgPrefix + + ': No role map found for domain=' + + domain + + ' so access denied' + ); + } else if (status === AccessCheckStatus.DENY_DOMAIN_EMPTY) { + winston.debug( + msgPrefix + + ': No policy assertions for domain=' + + domain + + ' so access denied' + ); + } - for (let role of roles) { - winston.debug(msgPrefix + ": Process role (" + role + ")"); + return cb(null, status); + } - let asserts = roleMap[role]; + static stripDomainPrefix(assertString, domain, defaultValue) { + this.init(); + let index = assertString.indexOf(':'); + if (index === -1) { + return assertString; + } - if (!asserts || asserts.length === 0) { - winston.debug(msgPrefix + ": No policy assertions in domain=" + domain + - " for role=" + role + " so access denied"); - continue; - } + if (assertString.substring(0, index) !== domain) { + return defaultValue; + } - if (this._matchAssertion(asserts, role, action, resource, msgPrefix)) { - return true; - } + return assertString.substring(index + 1); } - return false; - } - - static _actionByWildcardRole(action, domain, resource, roles, roleMap) { - let msgPrefix = "allowActionByRole: domain(" + domain + - ") action(" + action + ") resource(" + resource + ")"; - let keys = Object.keys(roleMap); - - for (let role of roles) { - winston.debug(msgPrefix + ": Process role (" + role + ")"); + static _actionByRole(action, domain, resource, roles, roleMap) { + let msgPrefix = + 'allowActionByRole: domain(' + + domain + + ') action(' + + action + + ') resource(' + + resource + + ')'; + + for (let role of roles) { + winston.debug(msgPrefix + ': Process role (' + role + ')'); + + let asserts = roleMap[role]; + + if (!asserts || asserts.length === 0) { + winston.debug( + msgPrefix + + ': No policy assertions in domain=' + + domain + + ' for role=' + + role + + ' so access denied' + ); + continue; + } + + if ( + this._matchAssertion(asserts, role, action, resource, msgPrefix) + ) { + return true; + } + } - for(let roleName of keys) { - let asserts = roleMap[roleName]; + return false; + } - if (!asserts || asserts.length === 0) { - winston.debug(msgPrefix + ": No policy assertions in domain=" + domain + - " for role=" + role + " so access denied"); - continue; + static _actionByWildcardRole(action, domain, resource, roles, roleMap) { + let msgPrefix = + 'allowActionByRole: domain(' + + domain + + ') action(' + + action + + ') resource(' + + resource + + ')'; + let keys = Object.keys(roleMap); + + for (let role of roles) { + winston.debug(msgPrefix + ': Process role (' + role + ')'); + + for (let roleName of keys) { + let asserts = roleMap[roleName]; + + if (!asserts || asserts.length === 0) { + winston.debug( + msgPrefix + + ': No policy assertions in domain=' + + domain + + ' for role=' + + role + + ' so access denied' + ); + continue; + } + + let assert = asserts[0]; + let matchStruct = assert.roleMatchStruct; + if (!matchStruct.matches(role)) { + const polName = assert.polName; + winston.debug( + msgPrefix + + ': policy(' + + polName + + ') regexpr-match: Failed: assert-role(' + + roleName + + ') doesnt match role(' + + role + + ')' + ); + continue; + } + + if ( + this._matchAssertion( + asserts, + roleName, + action, + resource, + msgPrefix + ) + ) { + return true; + } + } } - let assert = asserts[0]; - let matchStruct = assert.roleMatchStruct; - if (!matchStruct.matches(role)) { - const polName = assert.polName; - winston.debug(msgPrefix + ": policy(" + polName + - ") regexpr-match: Failed: assert-role(" + roleName + - ") doesnt match role(" + role + ")"); - continue; - } + return false; + } - if (this._matchAssertion(asserts, roleName, action, resource, msgPrefix)) { - return true; + static _matchAssertion(asserts, role, action, resource, msgPrefix) { + let matchStruct = null; + for (let assert of asserts) { + let assertAction = assert.action, + assertResource = assert.resource, + assertRole = assert.role, + polname = assert.polname; + + winston.debug( + msgPrefix + + ': Process Assertion: policy(' + + polname + + ') assert-action=' + + assertAction + + ' assert-resource=' + + assertResource + + ' assert-role=' + + assertRole + ); + + matchStruct = assert.actionMatchStruct; + if (!matchStruct.matches(action)) { + winston.debug( + msgPrefix + + ': policy(' + + polname + + ') regexpr-match: Failed: assert-action(' + + assertAction + + ") doesn't match action(" + + action + + ')' + ); + continue; + } + + matchStruct = assert.resourceMatchStruct; + if (!matchStruct.matches(resource)) { + winston.debug( + msgPrefix + + ': policy(' + + polname + + ') regexpr-match: Failed: assert-resource(' + + assertResource + + ") doesn't match resource(" + + resource + + ')' + ); + continue; + } + + winston.debug( + msgPrefix + + ': policy(' + + polname + + ') Matched: role(' + + role + + ') resource(' + + resource + + ') action(' + + action + + ')' + ); + return true; } - } - } - return false; - } - - static _matchAssertion(asserts, role, action, resource, msgPrefix) { - let matchStruct = null; - for (let assert of asserts) { - let assertAction = assert.action, - assertResource = assert.resource, - assertRole = assert.role, - polname = assert.polname; - - winston.debug(msgPrefix + ": Process Assertion: policy(" + polname + - ") assert-action=" + assertAction + - " assert-resource=" + assertResource + " assert-role=" + assertRole); - - matchStruct = assert.actionMatchStruct; - if (!matchStruct.matches(action)) { - winston.debug(msgPrefix + ": policy(" + polname + ") regexpr-match: Failed: assert-action(" + - assertAction + ") doesn't match action(" + action + ")"); - continue; - } - - matchStruct = assert.resourceMatchStruct; - if (!matchStruct.matches(resource)) { - winston.debug(msgPrefix + ": policy(" + polname + ") regexpr-match: Failed: assert-resource(" + - assertResource + ") doesn't match resource(" + resource + ")"); - continue; - } - - winston.debug(msgPrefix + ": policy(" + polname + ") Matched: role(" + role + - ") resource(" + resource + ") action(" + action + ")"); - return true; + return false; } - - return false; - } } module.exports = AuthZPEClient; diff --git a/clients/nodejs/zpe/src/PublicKeyStore.js b/clients/nodejs/zpe/src/PublicKeyStore.js index a663fef1725..2c7fca7392f 100644 --- a/clients/nodejs/zpe/src/PublicKeyStore.js +++ b/clients/nodejs/zpe/src/PublicKeyStore.js @@ -5,60 +5,61 @@ const YBase64 = require('@athenz/auth-core').YBase64; const fs = require('fs'); class PublicKeyStore { - constructor() { - this._ztsPublicKeyMap = {}; - this._zmsPublicKeyMap = {}; + constructor() { + this._ztsPublicKeyMap = {}; + this._zmsPublicKeyMap = {}; - let confFileName = config.confFileName; - if (!confFileName) { - let rootDir = process.env.ROOT; - if (!rootDir) { - rootDir = '/home/athenz'; - } - confFileName = rootDir + '/conf/athenz/athenz.conf'; - } - const conf = JSON.parse(fs.readFileSync(confFileName, 'utf8')); - - this._loadPublicKey(conf.zmsPublicKeys, this._zmsPublicKeyMap); - this._loadPublicKey(conf.ztsPublicKeys, this._ztsPublicKeyMap); - } + let confFileName = config.confFileName; + if (!confFileName) { + let rootDir = process.env.ROOT; + if (!rootDir) { + rootDir = '/home/athenz'; + } + confFileName = rootDir + '/conf/athenz/athenz.conf'; + } + const conf = JSON.parse(fs.readFileSync(confFileName, 'utf8')); - static setConfig(c) { - config = Object.assign({}, config, c.zpeClient); - } + this._loadPublicKey(conf.zmsPublicKeys, this._zmsPublicKeyMap); + this._loadPublicKey(conf.ztsPublicKeys, this._ztsPublicKeyMap); + } - _loadPublicKey(publicKeys, keyMap) { - if (!publicKeys) { - return; + static setConfig(c) { + config = Object.assign({}, config, c.zpeClient); } - for(let publicKey of publicKeys) { - let id = publicKey.id, key = publicKey.key; + _loadPublicKey(publicKeys, keyMap) { + if (!publicKeys) { + return; + } - if (!id || !key) { - continue; - } + for (let publicKey of publicKeys) { + let id = publicKey.id, + key = publicKey.key; - let pubKey = YBase64.ybase64Decode(key); - keyMap[id] = pubKey; - } - } + if (!id || !key) { + continue; + } - getZtsKey(keyId) { - if (!keyId) { - return null; + let pubKey = YBase64.ybase64Decode(key); + keyMap[id] = pubKey; + } } - return this._ztsPublicKeyMap[keyId]; - } + getZtsKey(keyId) { + if (!keyId) { + return null; + } - getZmsKey(keyId) { - if (!keyId) { - return null; + return this._ztsPublicKeyMap[keyId]; } - return this._zmsPublicKeyMap[keyId]; - } + getZmsKey(keyId) { + if (!keyId) { + return null; + } + + return this._zmsPublicKeyMap[keyId]; + } } module.exports = PublicKeyStore; diff --git a/clients/nodejs/zpe/src/ZPEMatch.js b/clients/nodejs/zpe/src/ZPEMatch.js index ae5fcc2540d..44ce5d4fe70 100644 --- a/clients/nodejs/zpe/src/ZPEMatch.js +++ b/clients/nodejs/zpe/src/ZPEMatch.js @@ -1,48 +1,50 @@ 'use strict'; const ZPEMatch = function (value) { - const equalMatches = function (val) { - return val === value; - }; - - const startswithMatches = function (val) { - return val.startsWith(value.substring(0, value.length - 1)); - }; - - const allMatches = function (val) { return true; }; - - const regexMatches = function (val) { - let replaced = '' + value; - replaced = replaced.replace(/([.+^$[\]\\(){}|-])/g, "\\$1"); - replaced = replaced.replace(/\?/g, '.').replace(/\*/g, '.*'); - replaced = `^${replaced}$`; - - const regexp = new RegExp(replaced); - - if (val.match(regexp)) { - return true; - } - return false; - }; - - return { - equal: { - name: 'equal', - matches: equalMatches - }, - startswith: { - name: 'startswith', - matches: startswithMatches - }, - all: { - name: 'all', - matches: allMatches - }, - regex: { - name: 'regex', - matches: regexMatches - } - }; + const equalMatches = function (val) { + return val === value; + }; + + const startswithMatches = function (val) { + return val.startsWith(value.substring(0, value.length - 1)); + }; + + const allMatches = function (val) { + return true; + }; + + const regexMatches = function (val) { + let replaced = '' + value; + replaced = replaced.replace(/([.+^$[\]\\(){}|-])/g, '\\$1'); + replaced = replaced.replace(/\?/g, '.').replace(/\*/g, '.*'); + replaced = `^${replaced}$`; + + const regexp = new RegExp(replaced); + + if (val.match(regexp)) { + return true; + } + return false; + }; + + return { + equal: { + name: 'equal', + matches: equalMatches, + }, + startswith: { + name: 'startswith', + matches: startswithMatches, + }, + all: { + name: 'all', + matches: allMatches, + }, + regex: { + name: 'regex', + matches: regexMatches, + }, + }; }; module.exports = ZPEMatch; diff --git a/clients/nodejs/zpe/src/ZPEUpdater.js b/clients/nodejs/zpe/src/ZPEUpdater.js index 70b9a90b7cb..b085472b0cc 100644 --- a/clients/nodejs/zpe/src/ZPEUpdater.js +++ b/clients/nodejs/zpe/src/ZPEUpdater.js @@ -12,266 +12,303 @@ let _policyCache = require('memory-cache'); let _zpeClt, _dirWatcher; class ZPEUpdater { - static setConfig(c) { - config = Object.assign({}, config, c.zpeClient); - } - - static setZPEClient(zpeClt) { - _zpeClt = zpeClt; - } - - static getRoleTokenCacheMap() { - return _roleCache; - } - - static getWildcardAllowAssertions(domain) { - let roleMap = this._lookupPolicyInCache(domain); - return roleMap.wildcardRoleAllowMap; - } - - static getRoleAllowAssertions(domain) { - let roleMap = this._lookupPolicyInCache(domain); - return roleMap.standardRoleAllowMap; - } - - static getWildcardDenyAssertions(domain) { - let roleMap = this._lookupPolicyInCache(domain); - return roleMap.wildcardRoleDenyMap; - } - - static getRoleDenyAssertions(domain) { - let roleMap = this._lookupPolicyInCache(domain); - return roleMap.standardRoleDenyMap; - } - - static watchPolicyDir() { - try { - _dirWatcher = fs.watch(config.policyDir, function(ev, fileName) { - if (fs.existsSync(config.policyDir + '/' + fileName) && ev === 'change') { - ZPEUpdater._loadFile(config.policyDir, fileName); - } - }); - } catch(e) { - winston.error("watchPolicyDir: invalid filePath: " + e.fileName); - _dirWatcher.close(); + static setConfig(c) { + config = Object.assign({}, config, c.zpeClient); } - } - static closeWatcher() { - if (!_dirWatcher) { - _dirWatcher.close(); + static setZPEClient(zpeClt) { + _zpeClt = zpeClt; } - } - - static close() { - _roleCache.clear(); - _policyCache.clear(); - } - static loadFiles(polDir) { - winston.debug("loadFiles: load start directory=" + polDir); - let files = fs.readdirSync(polDir) - .filter((fileName) => fileName.endsWith(".pol")); - - for (let fileName of files) { - this._loadFile(polDir, fileName); + static getRoleTokenCacheMap() { + return _roleCache; } - } - - static _loadFile(polDir, fileName) { - winston.debug("loadFiles: load start file name=" + fileName); - let path = polDir + '/' + fileName; - let spols = JSON.parse(fs.readFileSync(path, 'utf8')); - if (!spols) { - winston.error("_loadFile: unable to decode policy file=" + fileName); - return; + static getWildcardAllowAssertions(domain) { + let roleMap = this._lookupPolicyInCache(domain); + return roleMap.wildcardRoleAllowMap; } - let signedPolicyData = spols.signedPolicyData; - let signature = spols.signature; - let keyId = spols.keyId; - - // first let's verify the ZTS signature for our policy file - let verified = false; - - if (signedPolicyData) { - let pubKey = _zpeClt.getZtsPublicKey(keyId); - verified = Crypto.verify(this._asCanonicalString(signedPolicyData), pubKey, signature, 'SHA256'); + static getRoleAllowAssertions(domain) { + let roleMap = this._lookupPolicyInCache(domain); + return roleMap.standardRoleAllowMap; } - let policyData = null; - if (verified) { - // now let's verify that the ZMS signature for our policy file - policyData = signedPolicyData.policyData; - signature = signedPolicyData.zmsSignature; - keyId = signedPolicyData.zmsKeyId; - - if (policyData) { - let pubKey = _zpeClt.getZmsPublicKey(keyId); - verified = Crypto.verify(this._asCanonicalString(policyData), pubKey, signature, 'SHA256'); - } + static getWildcardDenyAssertions(domain) { + let roleMap = this._lookupPolicyInCache(domain); + return roleMap.wildcardRoleDenyMap; } - if (!verified) { - winston.error("loadFile: policy file=" + fileName + " is invalid"); - return; + static getRoleDenyAssertions(domain) { + let roleMap = this._lookupPolicyInCache(domain); + return roleMap.standardRoleDenyMap; } - this._loadPolicies(policyData, fileName); - } + static watchPolicyDir() { + try { + _dirWatcher = fs.watch(config.policyDir, function (ev, fileName) { + if ( + fs.existsSync(config.policyDir + '/' + fileName) && + ev === 'change' + ) { + ZPEUpdater._loadFile(config.policyDir, fileName); + } + }); + } catch (e) { + winston.error('watchPolicyDir: invalid filePath: ' + e.fileName); + _dirWatcher.close(); + } + } - static _loadPolicies(policyData, fileName) { - // HAVE: valid policy file - let domainName = policyData.domain; + static closeWatcher() { + if (!_dirWatcher) { + _dirWatcher.close(); + } + } - winston.debug("loadFile: policy file(" + fileName + ") for domain(" + domainName + ") is valid"); + static close() { + _roleCache.clear(); + _policyCache.clear(); + } - // Process the policies into assertions, process the assertions: action, resource, role - // If there is a wildcard in the action or resource, compile the - // regexp and place it into the assertion Struct. - // This is a performance enhancement for AuthZpeClient when it - // performs the authorization checks. + static loadFiles(polDir) { + winston.debug('loadFiles: load start directory=' + polDir); + let files = fs + .readdirSync(polDir) + .filter((fileName) => fileName.endsWith('.pol')); - let roleStandardAllowMap = {}; - let roleWildcardAllowMap = {}; - let roleStandardDenyMap = {}; - let roleWildcardDenyMap = {}; + for (let fileName of files) { + this._loadFile(polDir, fileName); + } + } - let policies = policyData.policies; + static _loadFile(polDir, fileName) { + winston.debug('loadFiles: load start file name=' + fileName); + let path = polDir + '/' + fileName; + let spols = JSON.parse(fs.readFileSync(path, 'utf8')); - for (let policy of policies) { - let assertions = policy.assertions; - let pname = policy.name; + if (!spols) { + winston.error( + '_loadFile: unable to decode policy file=' + fileName + ); + return; + } - winston.debug("loadFile: domain(" + domainName + ") policy(" + pname + ")"); + let signedPolicyData = spols.signedPolicyData; + let signature = spols.signature; + let keyId = spols.keyId; + + // first let's verify the ZTS signature for our policy file + let verified = false; + + if (signedPolicyData) { + let pubKey = _zpeClt.getZtsPublicKey(keyId); + verified = Crypto.verify( + this._asCanonicalString(signedPolicyData), + pubKey, + signature, + 'SHA256' + ); + } - if (!assertions) { - continue; - } + let policyData = null; + if (verified) { + // now let's verify that the ZMS signature for our policy file + policyData = signedPolicyData.policyData; + signature = signedPolicyData.zmsSignature; + keyId = signedPolicyData.zmsKeyId; + + if (policyData) { + let pubKey = _zpeClt.getZmsPublicKey(keyId); + verified = Crypto.verify( + this._asCanonicalString(policyData), + pubKey, + signature, + 'SHA256' + ); + } + } - for (let assertion of assertions) { - let assert = {}; - assert.polname = pname; + if (!verified) { + winston.error('loadFile: policy file=' + fileName + ' is invalid'); + return; + } - assert.action = assertion.action; - assert.actionMatchStruct = this._getMatchObject(assert.action); + this._loadPolicies(policyData, fileName); + } - assert.resource = _zpeClt.stripDomainPrefix(assertion.resource, domainName, assertion.resource); - assert.resourceMatchStruct = this._getMatchObject(assert.resource); + static _loadPolicies(policyData, fileName) { + // HAVE: valid policy file + let domainName = policyData.domain; + + winston.debug( + 'loadFile: policy file(' + + fileName + + ') for domain(' + + domainName + + ') is valid' + ); + + // Process the policies into assertions, process the assertions: action, resource, role + // If there is a wildcard in the action or resource, compile the + // regexp and place it into the assertion Struct. + // This is a performance enhancement for AuthZpeClient when it + // performs the authorization checks. + + let roleStandardAllowMap = {}; + let roleWildcardAllowMap = {}; + let roleStandardDenyMap = {}; + let roleWildcardDenyMap = {}; + + let policies = policyData.policies; + + for (let policy of policies) { + let assertions = policy.assertions; + let pname = policy.name; + + winston.debug( + 'loadFile: domain(' + domainName + ') policy(' + pname + ')' + ); + + if (!assertions) { + continue; + } + + for (let assertion of assertions) { + let assert = {}; + assert.polname = pname; + + assert.action = assertion.action; + assert.actionMatchStruct = this._getMatchObject(assert.action); + + assert.resource = _zpeClt.stripDomainPrefix( + assertion.resource, + domainName, + assertion.resource + ); + assert.resourceMatchStruct = this._getMatchObject( + assert.resource + ); + + assert.role = _zpeClt.stripDomainPrefix( + assertion.role, + domainName, + assertion.role + ); + // strip the prefix "role." too + assert.role = assert.role.replace(/^role\./, ''); + assert.roleMatchStruct = this._getMatchObject(assert.role); + + let matchStruct = assert.roleMatchStruct; + let roleMap = null; + + if (assertion.effect === 'DENY') { + if (matchStruct.name === 'equal') { + roleMap = roleStandardDenyMap; + } else { + roleMap = roleWildcardDenyMap; + } + } else { + if (matchStruct.name === 'equal') { + roleMap = roleStandardAllowMap; + } else { + roleMap = roleWildcardAllowMap; + } + } + + let assertList = roleMap[assert.role]; + if (!assertList) { + roleMap[assert.role] = []; + } + roleMap[assert.role].push(assert); + } + } - assert.role = _zpeClt.stripDomainPrefix(assertion.role, domainName, assertion.role); - // strip the prefix "role." too - assert.role = assert.role.replace(/^role\./, ''); - assert.roleMatchStruct = this._getMatchObject(assert.role); + _policyCache.put( + domainName, + { + standardRoleAllowMap: roleStandardAllowMap, + wildcardRoleAllowMap: roleWildcardAllowMap, + standardRoleDenyMap: roleStandardDenyMap, + wildcardRoleDenyMap: roleWildcardDenyMap, + }, + config.policyRefresh * 1000 + ); + } - let matchStruct = assert.roleMatchStruct; - let roleMap = null; + static _lookupPolicyInCache(domain) { + let roleMap = _policyCache.get(domain); - if (assertion.effect === 'DENY') { - if (matchStruct.name === 'equal') { - roleMap = roleStandardDenyMap; - } else { - roleMap = roleWildcardDenyMap; - } + if (!roleMap) { + winston.debug('_lookupPolicyInCache: Policy Cache Miss'); + this.loadFiles(config.policyDir); } else { - if (matchStruct.name === 'equal') { - roleMap = roleStandardAllowMap; - } else { - roleMap = roleWildcardAllowMap; - } + winston.debug('_lookupPolicyInCache: Policy Cache Hit'); } - let assertList = roleMap[assert.role]; - if (!assertList) { - roleMap[assert.role] = []; - } - roleMap[assert.role].push(assert); - } + return _policyCache.get(domain) || {}; } - _policyCache.put( - domainName, - { - standardRoleAllowMap: roleStandardAllowMap, - wildcardRoleAllowMap: roleWildcardAllowMap, - standardRoleDenyMap: roleStandardDenyMap, - wildcardRoleDenyMap: roleWildcardDenyMap - }, - config.policyRefresh * 1000 - ); - } - - static _lookupPolicyInCache(domain) { - let roleMap = _policyCache.get(domain); - - if (!roleMap) { - winston.debug("_lookupPolicyInCache: Policy Cache Miss"); - this.loadFiles(config.policyDir); - } else { - winston.debug("_lookupPolicyInCache: Policy Cache Hit"); - } + static _asCanonicalString(obj) { + let str = ''; + if (typeof obj === 'object' && !Array.isArray(obj)) { + str += '{'; + for (let key of Object.keys(obj).sort()) { + str += '"' + key + '":' + this._asCanonicalString(obj[key]); + str += ','; + } + str = this._deleteEndSeparator(str); + str += '}'; + } else if (typeof obj === 'object') { + str += '['; + for (let item of obj) { + str += this._asCanonicalString(item); + str += ','; + } + str = this._deleteEndSeparator(str); + str += ']'; + } else if (typeof obj === 'string') { + str += '"' + obj + '"'; + } else if (typeof obj === 'number') { + str += String(obj); + } - return _policyCache.get(domain) || {}; - } - - static _asCanonicalString(obj) { - let str = ''; - if (typeof(obj) === 'object' && !Array.isArray(obj)) { - str += '{'; - for (let key of Object.keys(obj).sort()) { - str += '"' + key + '":' + this._asCanonicalString(obj[key]); - str += ","; - } - str = this._deleteEndSeparator(str); - str += '}'; - } else if (typeof(obj) === 'object') { - str += '['; - for(let item of obj) { - str += this._asCanonicalString(item); - str += ','; - } - str = this._deleteEndSeparator(str); - str += ']'; - } else if (typeof(obj) === 'string') { - str += '"' + obj + '"'; - } else if (typeof(obj) === 'number') { - str += String(obj); + return str; } - return str; - } + static _deleteEndSeparator(str) { + if (str[str.length - 1] === ',') { + str = str.substring(0, str.length - 1); + } - static _deleteEndSeparator(str) { - if (str[str.length - 1] === ',') { - str = str.substring(0, str.length - 1); + return str; } - return str; - } - - static _getMatchObject(value) { - let res = {}; - let matches = null; - const matchGen = ZPEMatch(value); - - if (value === '*') { - res = matchGen.all; - } else { - let anyCharMatch = value.indexOf('*'); - let singleCharMatch = value.indexOf('?'); - - if (anyCharMatch === -1 && singleCharMatch === -1) { - res = matchGen.equal; - } else if (anyCharMatch === value.length - 1 && singleCharMatch === -1) { - res = matchGen.startswith; - } else { - res = matchGen.regex; - } - } + static _getMatchObject(value) { + let res = {}; + let matches = null; + const matchGen = ZPEMatch(value); - return res; - } + if (value === '*') { + res = matchGen.all; + } else { + let anyCharMatch = value.indexOf('*'); + let singleCharMatch = value.indexOf('?'); + + if (anyCharMatch === -1 && singleCharMatch === -1) { + res = matchGen.equal; + } else if ( + anyCharMatch === value.length - 1 && + singleCharMatch === -1 + ) { + res = matchGen.startswith; + } else { + res = matchGen.regex; + } + } + + return res; + } } module.exports = ZPEUpdater; diff --git a/clients/nodejs/zpe/test/config/IdentityMock.js b/clients/nodejs/zpe/test/config/IdentityMock.js index edcc3d9e234..11215fb0d26 100644 --- a/clients/nodejs/zpe/test/config/IdentityMock.js +++ b/clients/nodejs/zpe/test/config/IdentityMock.js @@ -14,38 +14,38 @@ 'use strict'; class IdentityMock { - constructor(domain, name) { - this._domain = domain; - this._name = name; - - this._creds = null; - if(domain && name) { - this._creds = 'v=S1;d=' + domain + ';n=' + name + ';'; + constructor(domain, name) { + this._domain = domain; + this._name = name; + + this._creds = null; + if (domain && name) { + this._creds = 'v=S1;d=' + domain + ';n=' + name + ';'; + } } - } - getDomain() { - return this._domain; - } + getDomain() { + return this._domain; + } - getName() { - return this._name; - } + getName() { + return this._name; + } - getCredentials() { - return this._creds; - } + getCredentials() { + return this._creds; + } - getAuthority() { - if (!this._domain){ - return null; + getAuthority() { + if (!this._domain) { + return null; + } + return this; } - return this; - } - getHeader() { - return 'Athenz-Principal-Auth'; - } + getHeader() { + return 'Athenz-Principal-Auth'; + } } module.exports = IdentityMock; diff --git a/clients/nodejs/zpe/test/config/KeyStore.js b/clients/nodejs/zpe/test/config/KeyStore.js index 1788cfca0c2..eb0a549503d 100644 --- a/clients/nodejs/zpe/test/config/KeyStore.js +++ b/clients/nodejs/zpe/test/config/KeyStore.js @@ -3,12 +3,12 @@ var fs = require('fs'); class KeyStore { - static getPublicKey(domain, service, keyId) { - if (!domain || !service || !keyId) { - return null; + static getPublicKey(domain, service, keyId) { + if (!domain || !service || !keyId) { + return null; + } + return fs.readFileSync(__dirname + '/../resources/public_k0.pem'); } - return fs.readFileSync(__dirname + '/../resources/public_k0.pem'); - } } module.exports = KeyStore; diff --git a/clients/nodejs/zpe/test/config/RdlMock.js b/clients/nodejs/zpe/test/config/RdlMock.js index 095af1e06d7..8994680c260 100644 --- a/clients/nodejs/zpe/test/config/RdlMock.js +++ b/clients/nodejs/zpe/test/config/RdlMock.js @@ -14,17 +14,17 @@ 'use strict'; class rdlMock { - constructor(res, flag) { - this._res = res; - this._flag = flag; - } + constructor(res, flag) { + this._res = res; + this._flag = flag; + } - getRoleToken(params, cb) { - if(!this._flag) { - cb(new Error('rdlMock: Error'), null); + getRoleToken(params, cb) { + if (!this._flag) { + cb(new Error('rdlMock: Error'), null); + } + cb(null, this._res); } - cb(null, this._res); - } } module.exports = rdlMock; diff --git a/clients/nodejs/zpe/test/config/RoleTokenMock.js b/clients/nodejs/zpe/test/config/RoleTokenMock.js index 341d622f8d0..0524fdc6c5b 100644 --- a/clients/nodejs/zpe/test/config/RoleTokenMock.js +++ b/clients/nodejs/zpe/test/config/RoleTokenMock.js @@ -14,13 +14,13 @@ 'use strict'; class RoleTokenMock { - constructor(expiryTime) { - this._expiryTime = expiryTime; - } + constructor(expiryTime) { + this._expiryTime = expiryTime; + } - getExpiryTime() { - return Math.floor(Date.now() / 1000) + this._expiryTime; - } + getExpiryTime() { + return Math.floor(Date.now() / 1000) + this._expiryTime; + } } module.exports = RoleTokenMock; diff --git a/clients/nodejs/zpe/test/config/SiaProviderMock.js b/clients/nodejs/zpe/test/config/SiaProviderMock.js index ace08c3165f..83d419aa890 100644 --- a/clients/nodejs/zpe/test/config/SiaProviderMock.js +++ b/clients/nodejs/zpe/test/config/SiaProviderMock.js @@ -16,24 +16,24 @@ var identityMock = require('./IdentityMock'); class SiaProviderMock { - constructor(domain, service) { - this._domain = null; - this._service = null; + constructor(domain, service) { + this._domain = null; + this._service = null; - if(domain) { - this._domain = domain.toString().toLowerCase(); + if (domain) { + this._domain = domain.toString().toLowerCase(); + } + if (service) { + this._service = service.toString().toLowerCase(); + } } - if(service) { - this._service = service.toString().toLowerCase(); - } - } - getIdentity(domain, service) { - if (domain !== this._domain || service !== this._service) { - return null; + getIdentity(domain, service) { + if (domain !== this._domain || service !== this._service) { + return null; + } + return new identityMock(this._domain, this._service); } - return new identityMock(this._domain, this._service); - } } module.exports = SiaProviderMock; diff --git a/clients/nodejs/zpe/test/config/helpers.js b/clients/nodejs/zpe/test/config/helpers.js index 003dca72fd2..5492d70b626 100644 --- a/clients/nodejs/zpe/test/config/helpers.js +++ b/clients/nodejs/zpe/test/config/helpers.js @@ -1,9 +1,11 @@ 'use strict'; -var privateKey = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBcGZoVnhPT2VaQVhMNkxhYzY3dXFXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOCk53UWs5cU1XSVNHRDZ3NSt4aW5OOUZ2YWtXRndsblJhY1JmaWtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWoKMzlhdXM0aFZuK2lCZjZWVFZWN0tyTVdQMnRkRDkybW4xdTJTM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UgpBQzVVbnpTKzZlTjF2RTRxOTNNMkdXSk1qcVVaZXQ1RHJwQVcrWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5Cnd3SEJDS2lDNW9xNmJWVXlLOUNBay9qejJYSW5oRjB2T0RtQlJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlEKZFJlYVF0dWt3R3JLK0EvdGZBVFYvSWs5Tk14Um95V2FUd2o4MFFJREFRQUJBb0lCQUZTQ0lGb0NkSElGcVpBeQorNi9rZkpjalMwRTlhMlU3VC9SZ2dZREpteDVnTmV6L1JtM1BCR3M1RzFsL1VVWnN1UXg1Mk02bHZxTHI5SUlICmVNM0JGYk1SaHl6QWYycEZ2ajlnbUtTeEJWWkFISlhHY2hwVm05cjZmbTdQMnJCK0kvS0NNN3pKVTdoZ2g5RUEKMFY5VTFNV2I4U0JHUmhPdmh1UW9zZCtMNkpxQmYzOUkrN1BWdFRxNjZuNXVtWGU1WlU1eTJiMmRsSmRTU1hXeQpjc1BSMWUzT1IvTnM5dDVJTVdHcnR2VUowWHlWMjM3eENVM2tmOEg4aDA0NWd6eUdrZHE5djZIVHJhQzZMaTF5CmdwRDhVMFQ2VVQwWHBGcWowbGttTk1NcHNqOWw1QnVSR2ZPNjVhb0ZBOWl5S2xMdndaTnJ2YnNZL0UrNUJjRnMKQWNJczY2a0NnWUVBenAxU1VZZ2FGaWZFQnE3cGluNTY3S3FQemFCcURLYUZTekhkWnFsR1RDVzJ4bDJIY2J4YwpObWJseXUrU21rdzFQZGFlbzF3SXh3U052eU8xWTE1Y0pGbzBteUljUzZRZFJFZmhwWjU0dUdqOE9YQ0RzeW10Ci94TVEwVVlzdFJPaXhDVTlVL1lFdnh3TzlpUkRpU0VsdXd3cDBuN09LdjRRTTVxVkliMFdydjhDZ1lFQXphUDkKbW9ROFFDNS9mRlg0eE5tOFFGOXp5Ukc3SnRndysrNWJYbkFXVFgzcWpIaUxlQ1ZZdHpidlFVY1g0cFp5YlRqaAowYm5yUXpiZmJFVFFZMnk4ZGdqWTVjMEpPNmNTVm11QkE4Lzc4aHl4WUZTTzV0N01CSS93ak42aU80VWhHSGdKCnc3Q1VVM013RDMwWlNZQ3JwNDZyZE5yamdUc3dmN2crc3N1OUpDOENnWUJvMjFhbm9oYjdIM3RRbVB4VkdSTngKZ0s0eWdUTFE4TUc5QTdXRklHdFl3ZHNjbU9MZ1NlUFNpQzRlNjY3UE45WGRhRXBpUlpiK3ljVFdPRjBaN1ExKwpOWGwxTWI2Q2RPdVZkNVdBNUFnSUx0K3lsdk4vdmF0Y1JHVElrSUNuOVNzcHVHeURhOXZFMFl5V1Jwa3Z3dTdQCkdzRXUzc1BxOWIxck13eDBidTVRS1FLQmdBeU1TQ3BJaldDaE5iaEJpcmVBVGNOano2M2lQaGhGc3Q5OGtPaTMKVURVVVRONmJjRzg1WUN0MTE2MlZCL2tVa3hEbEdxcHdmTkdTSkpuM3JQdVVJLy9UMUdCWlhZbmRUUG9tL3kxYgpZSlZLZU94VzNJMXI1T2tXVzJoTklYc2VTWUd6dVd6T2RvNk5CYzY4SkhIZXZ4cXZVdmtEYmtSeGR5a2o5ZmQxCkJTcVRBb0dBVG1nZ05MQk5tNzh0ZWg1cGVyY2RTSXV2VWFMNzRzaXlKbFJlWW5ETitEeUQyUDMwaE9VVUVSYWEKVm9yaFlLdTdTdWVHclBhRy8vYWxZa3hGTGs1Q0RkVmovMWdaS3dTTWc0L3RaRm1yYXBOMFVmdDdGQkx1N1RYTApab1NSeVFsZFo0ZGRpR0xsQ1hlbGh4STdjSnJoQlN3UytYcVVPdnk0V29oYTdsYndYems9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg--"; -var publicKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwZmhWeE9PZVpBWEw2TGFjNjd1cQpXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOTndRazlxTVdJU0dENnc1K3hpbk45RnZha1dGd2xuUmFjUmZpCmtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWozOWF1czRoVm4raUJmNlZUVlY3S3JNV1AydGREOTJtbjF1MlMKM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UkFDNVVuelMrNmVOMXZFNHE5M00yR1dKTWpxVVpldDVEcnBBVworWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5d3dIQkNLaUM1b3E2YlZVeUs5Q0FrL2p6MlhJbmhGMHZPRG1CClJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlFkUmVhUXR1a3dHcksrQS90ZkFUVi9JazlOTXhSb3lXYVR3ajgKMFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--"; +var privateKey = + 'LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBcGZoVnhPT2VaQVhMNkxhYzY3dXFXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOCk53UWs5cU1XSVNHRDZ3NSt4aW5OOUZ2YWtXRndsblJhY1JmaWtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWoKMzlhdXM0aFZuK2lCZjZWVFZWN0tyTVdQMnRkRDkybW4xdTJTM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UgpBQzVVbnpTKzZlTjF2RTRxOTNNMkdXSk1qcVVaZXQ1RHJwQVcrWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5Cnd3SEJDS2lDNW9xNmJWVXlLOUNBay9qejJYSW5oRjB2T0RtQlJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlEKZFJlYVF0dWt3R3JLK0EvdGZBVFYvSWs5Tk14Um95V2FUd2o4MFFJREFRQUJBb0lCQUZTQ0lGb0NkSElGcVpBeQorNi9rZkpjalMwRTlhMlU3VC9SZ2dZREpteDVnTmV6L1JtM1BCR3M1RzFsL1VVWnN1UXg1Mk02bHZxTHI5SUlICmVNM0JGYk1SaHl6QWYycEZ2ajlnbUtTeEJWWkFISlhHY2hwVm05cjZmbTdQMnJCK0kvS0NNN3pKVTdoZ2g5RUEKMFY5VTFNV2I4U0JHUmhPdmh1UW9zZCtMNkpxQmYzOUkrN1BWdFRxNjZuNXVtWGU1WlU1eTJiMmRsSmRTU1hXeQpjc1BSMWUzT1IvTnM5dDVJTVdHcnR2VUowWHlWMjM3eENVM2tmOEg4aDA0NWd6eUdrZHE5djZIVHJhQzZMaTF5CmdwRDhVMFQ2VVQwWHBGcWowbGttTk1NcHNqOWw1QnVSR2ZPNjVhb0ZBOWl5S2xMdndaTnJ2YnNZL0UrNUJjRnMKQWNJczY2a0NnWUVBenAxU1VZZ2FGaWZFQnE3cGluNTY3S3FQemFCcURLYUZTekhkWnFsR1RDVzJ4bDJIY2J4YwpObWJseXUrU21rdzFQZGFlbzF3SXh3U052eU8xWTE1Y0pGbzBteUljUzZRZFJFZmhwWjU0dUdqOE9YQ0RzeW10Ci94TVEwVVlzdFJPaXhDVTlVL1lFdnh3TzlpUkRpU0VsdXd3cDBuN09LdjRRTTVxVkliMFdydjhDZ1lFQXphUDkKbW9ROFFDNS9mRlg0eE5tOFFGOXp5Ukc3SnRndysrNWJYbkFXVFgzcWpIaUxlQ1ZZdHpidlFVY1g0cFp5YlRqaAowYm5yUXpiZmJFVFFZMnk4ZGdqWTVjMEpPNmNTVm11QkE4Lzc4aHl4WUZTTzV0N01CSS93ak42aU80VWhHSGdKCnc3Q1VVM013RDMwWlNZQ3JwNDZyZE5yamdUc3dmN2crc3N1OUpDOENnWUJvMjFhbm9oYjdIM3RRbVB4VkdSTngKZ0s0eWdUTFE4TUc5QTdXRklHdFl3ZHNjbU9MZ1NlUFNpQzRlNjY3UE45WGRhRXBpUlpiK3ljVFdPRjBaN1ExKwpOWGwxTWI2Q2RPdVZkNVdBNUFnSUx0K3lsdk4vdmF0Y1JHVElrSUNuOVNzcHVHeURhOXZFMFl5V1Jwa3Z3dTdQCkdzRXUzc1BxOWIxck13eDBidTVRS1FLQmdBeU1TQ3BJaldDaE5iaEJpcmVBVGNOano2M2lQaGhGc3Q5OGtPaTMKVURVVVRONmJjRzg1WUN0MTE2MlZCL2tVa3hEbEdxcHdmTkdTSkpuM3JQdVVJLy9UMUdCWlhZbmRUUG9tL3kxYgpZSlZLZU94VzNJMXI1T2tXVzJoTklYc2VTWUd6dVd6T2RvNk5CYzY4SkhIZXZ4cXZVdmtEYmtSeGR5a2o5ZmQxCkJTcVRBb0dBVG1nZ05MQk5tNzh0ZWg1cGVyY2RTSXV2VWFMNzRzaXlKbFJlWW5ETitEeUQyUDMwaE9VVUVSYWEKVm9yaFlLdTdTdWVHclBhRy8vYWxZa3hGTGs1Q0RkVmovMWdaS3dTTWc0L3RaRm1yYXBOMFVmdDdGQkx1N1RYTApab1NSeVFsZFo0ZGRpR0xsQ1hlbGh4STdjSnJoQlN3UytYcVVPdnk0V29oYTdsYndYems9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg--'; +var publicKey = + 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwZmhWeE9PZVpBWEw2TGFjNjd1cQpXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOTndRazlxTVdJU0dENnc1K3hpbk45RnZha1dGd2xuUmFjUmZpCmtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWozOWF1czRoVm4raUJmNlZUVlY3S3JNV1AydGREOTJtbjF1MlMKM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UkFDNVVuelMrNmVOMXZFNHE5M00yR1dKTWpxVVpldDVEcnBBVworWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5d3dIQkNLaUM1b3E2YlZVeUs5Q0FrL2p6MlhJbmhGMHZPRG1CClJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlFkUmVhUXR1a3dHcksrQS90ZkFUVi9JazlOTXhSb3lXYVR3ajgKMFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--'; module.exports = { - privateKey: privateKey, - publicKey: publicKey + privateKey: privateKey, + publicKey: publicKey, }; diff --git a/clients/nodejs/zpe/test/unit/AccessCheckStatus.js b/clients/nodejs/zpe/test/unit/AccessCheckStatus.js index 74471c3c8d2..b2ddc5fcb53 100644 --- a/clients/nodejs/zpe/test/unit/AccessCheckStatus.js +++ b/clients/nodejs/zpe/test/unit/AccessCheckStatus.js @@ -27,11 +27,11 @@ var rdlMock = require('../config/RdlMock'); var sandbox; var params = { - zts: 'zts.athenz.com', - domainName: null, - serviceName: null, - siaProvider: null, - identity: null + zts: 'zts.athenz.com', + domainName: null, + serviceName: null, + siaProvider: null, + identity: null, }; var domainName = 'athenz.user'; @@ -41,23 +41,22 @@ var siaMock = new siaProviderMock(domainName, serviceName); var ideMock = new identityMock(domainName, serviceName); var siaParams = { - domainName: domainName, - serviceName: serviceName, - siaProvider: siaMock, - identity: null + domainName: domainName, + serviceName: serviceName, + siaProvider: siaMock, + identity: null, }; var cacheKey = 'cacheKeyRoleToken'; -describe('AccessCheckStatus', function() { - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); +describe('AccessCheckStatus', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); - afterEach(function() { - sandbox.restore(); - }); + afterEach(function () { + sandbox.restore(); + }); - it('should test AccessCheckStatus', function() { - }); + it('should test AccessCheckStatus', function () {}); }); diff --git a/clients/nodejs/zpe/test/unit/AuthZPEClient.js b/clients/nodejs/zpe/test/unit/AuthZPEClient.js index 152f8362288..a9bb93c18a8 100644 --- a/clients/nodejs/zpe/test/unit/AuthZPEClient.js +++ b/clients/nodejs/zpe/test/unit/AuthZPEClient.js @@ -20,7 +20,12 @@ var AuthZPEClient = require('../../src/AuthZPEClient'); var RoleToken = require('@athenz/auth-core').RoleToken; var YBase64 = require('@athenz/auth-core').YBase64; -var privateKeyK0 = Buffer.from(fs.readFileSync(process.cwd() + '/test/resources/unit_test_private_k0.pem', 'utf8')); +var privateKeyK0 = Buffer.from( + fs.readFileSync( + process.cwd() + '/test/resources/unit_test_private_k0.pem', + 'utf8' + ) +); var policyDir = process.cwd() + '/test/resources/pol'; var confFileName = process.cwd() + '/test/resources/athenz.conf'; @@ -29,102 +34,118 @@ var expect = require('chai').expect; var sandbox; AuthZPEClient.setConfig({ - zpeClient: { - logLevel: 'debug', - policyDir: policyDir, - confFileName: confFileName, - tokenRefresh: 1800, - policyRefresh: 1800, - allowedOffset: 300, - disableCache: false, - updater: './ZPEUpdater', - disableWatch: true - } + zpeClient: { + logLevel: 'debug', + policyDir: policyDir, + confFileName: confFileName, + tokenRefresh: 1800, + policyRefresh: 1800, + allowedOffset: 300, + disableCache: false, + updater: './ZPEUpdater', + disableWatch: true, + }, }); // RoleTokens var roleTokenParams = { - version: 'Z1', - domain: 'athenz.test', - roles: ['users'] + version: 'Z1', + domain: 'athenz.test', + roles: ['users'], }; var rToken = new RoleToken(roleTokenParams); rToken.sign(privateKeyK0); var roleToken = rToken.getSignedToken(); var wildcardRoleTokenParams = { - version: 'Z1', - domain: 'athenz.test', - roles: ['wildcardroletest'] + version: 'Z1', + domain: 'athenz.test', + roles: ['wildcardroletest'], }; var wildcardRToken = new RoleToken(wildcardRoleTokenParams); wildcardRToken.sign(privateKeyK0); var wildcardRoleToken = wildcardRToken.getSignedToken(); var noSuchRoleTokenParams = { - version: 'Z1', - domain: 'athenz.test', - roles: ['nosuchrole'] + version: 'Z1', + domain: 'athenz.test', + roles: ['nosuchrole'], }; var nRToken = new RoleToken(noSuchRoleTokenParams); nRToken.sign(privateKeyK0); var noSuchRoleToken = nRToken.getSignedToken(); var emptyRoleTokenParams = { - version: 'Z1', - domain: 'athenz.test.empty', - roles: ['users'] + version: 'Z1', + domain: 'athenz.test.empty', + roles: ['users'], }; var eRToken = new RoleToken(emptyRoleTokenParams); eRToken.sign(privateKeyK0); var emptyRoleToken = eRToken.getSignedToken(); var expiredRoleTokenParams = { - version: 'Z1', - domain: 'athenz.test', - roles: ['users'] + version: 'Z1', + domain: 'athenz.test', + roles: ['users'], }; -var expiredRToken = new RoleToken('v=Z1;d=athenz.test;r=users;a=2fff22fa;t=1521813351;e=1521813351;k=0;s=dummy'); +var expiredRToken = new RoleToken( + 'v=Z1;d=athenz.test;r=users;a=2fff22fa;t=1521813351;e=1521813351;k=0;s=dummy' +); expiredRToken.sign(privateKeyK0); var expiredRoleToken = expiredRToken.getSignedToken(); console.log(expiredRoleToken); // RoleTokens -describe('AuthZPEClient', function() { - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); +describe('AuthZPEClient', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); - afterEach(function() { - AuthZPEClient.setConfig({ - zpeClient: { - logLevel: 'debug', - policyDir: policyDir, - confFileName: confFileName, - tokenRefresh: 1800, - policyRefresh: 1800, - allowedOffset: 300, - disableCache: false, - updater: './ZPEUpdater', - disableWatch: true - } + afterEach(function () { + AuthZPEClient.setConfig({ + zpeClient: { + logLevel: 'debug', + policyDir: policyDir, + confFileName: confFileName, + tokenRefresh: 1800, + policyRefresh: 1800, + allowedOffset: 300, + disableCache: false, + updater: './ZPEUpdater', + disableWatch: true, + }, + }); + sandbox.restore(); }); - sandbox.restore(); - }); - it('should test AuthZPEClient getZmsPublicKey', function() { - expect(AuthZPEClient.getZmsPublicKey('0')).to.deep.equal(YBase64.ybase64Decode('LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--')); - }); + it('should test AuthZPEClient getZmsPublicKey', function () { + expect(AuthZPEClient.getZmsPublicKey('0')).to.deep.equal( + YBase64.ybase64Decode( + 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--' + ) + ); + }); - it('should test AuthZPEClient getZtsPublicKey', function() { - expect(AuthZPEClient.getZtsPublicKey('0')).to.deep.equal(YBase64.ybase64Decode('LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--')); - }); + it('should test AuthZPEClient getZtsPublicKey', function () { + expect(AuthZPEClient.getZtsPublicKey('0')).to.deep.equal( + YBase64.ybase64Decode( + 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--' + ) + ); + }); - it('should test AuthZPEClient stripDomainPrefix', function() { - expect(AuthZPEClient.stripDomainPrefix('athenz.test:resource', 'athenz.test', 'defaultDomain')).to.equal('resource'); - }); + it('should test AuthZPEClient stripDomainPrefix', function () { + expect( + AuthZPEClient.stripDomainPrefix( + 'athenz.test:resource', + 'athenz.test', + 'defaultDomain' + ) + ).to.equal('resource'); + }); - /* + /* ALLOW: "Access Check was explicitly allowed", DENY: "Access Check was explicitly denied", DENY_NO_MATCH: "Access denied due to no match to any of the assertions defined in domain policy file", @@ -136,310 +157,348 @@ describe('AuthZPEClient', function() { DENY_DOMAIN_EMPTY: "Access denied due to no policies in the domain file", DENY_INVALID_PARAMETERS: "Access denied due to invalid/empty action/resource values", */ - it('should test AuthZPEClient allowAccess expecting result ALLOW (Begin-with match)', function() { - var resource = 'athenz.test:testresgroup.allow'; - var action = 'read'; - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access Check was explicitly allowed'); - } - ); - - resource = 'athenz.test:testresgroup.allow'; - action = 'readandwrite'; - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access Check was explicitly allowed'); - } - ); - - resource = 'athenz.test:testresgroup.allow'; - action = 'read'; - AuthZPEClient.allowAccess( - { - roleToken: wildcardRoleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access Check was explicitly allowed'); - } - ); - }); - - it('should test authzpeclient allowaccess expecting result deny with dot (begin-with match)', function() { - //var resource = 'athenz.test:testresgroup.'; - var resource = 'athenz.test:testresgroupa'; - var action = 'read'; - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access denied due to no match to any of the assertions defined in domain policy file'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result ALLOW (End-with match)', function() { - var resource = 'allow.testresgroup'; - var action = 'read'; - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access Check was explicitly allowed'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result ALLOW', function() { - var resource = 'allow.testresgroup'; - var action = 'read'; - AuthZPEClient.setConfig({ - zpeClient: { - logLevel: 'debug', - policyDir: policyDir, - confFileName: confFileName, - tokenRefresh: 1800, - policyRefresh: 1800, - allowedOffset: 300, - disableCache: false, - updater: './ZPEUpdater', - disableWatch: false - } + it('should test AuthZPEClient allowAccess expecting result ALLOW (Begin-with match)', function () { + var resource = 'athenz.test:testresgroup.allow'; + var action = 'read'; + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access Check was explicitly allowed' + ); + } + ); + + resource = 'athenz.test:testresgroup.allow'; + action = 'readandwrite'; + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access Check was explicitly allowed' + ); + } + ); + + resource = 'athenz.test:testresgroup.allow'; + action = 'read'; + AuthZPEClient.allowAccess( + { + roleToken: wildcardRoleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access Check was explicitly allowed' + ); + } + ); + }); + + it('should test authzpeclient allowaccess expecting result deny with dot (begin-with match)', function () { + //var resource = 'athenz.test:testresgroup.'; + var resource = 'athenz.test:testresgroupa'; + var action = 'read'; + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access denied due to no match to any of the assertions defined in domain policy file' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result ALLOW (End-with match)', function () { + var resource = 'allow.testresgroup'; + var action = 'read'; + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access Check was explicitly allowed' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result ALLOW', function () { + var resource = 'allow.testresgroup'; + var action = 'read'; + AuthZPEClient.setConfig({ + zpeClient: { + logLevel: 'debug', + policyDir: policyDir, + confFileName: confFileName, + tokenRefresh: 1800, + policyRefresh: 1800, + allowedOffset: 300, + disableCache: false, + updater: './ZPEUpdater', + disableWatch: false, + }, + }); + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access Check was explicitly allowed' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result DENY', function () { + var resource = 'athenz.test:testresgroup.deny'; + var action = 'write'; + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access Check was explicitly denied' + ); + } + ); + + resource = 'athenz.test:testresgroup.deny'; + action = 'write'; + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access Check was explicitly denied' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result DENY_NO_MATCH', function () { + var resource = 'athenz.test:nosuchresource'; + var action = 'read'; + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access denied due to no match to any of the assertions defined in domain policy file' + ); + } + ); + + resource = 'allow.testresgroup'; + action = 'read'; + AuthZPEClient.allowAccess( + { + roleToken: noSuchRoleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access denied due to no match to any of the assertions defined in domain policy file' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result DENY_DOMAIN_EMPTY', function () { + var resource = 'allow.testresgroup'; + var action = 'read'; + AuthZPEClient.allowAccess( + { + roleToken: emptyRoleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access denied due to no policies in the domain file' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result DENY_ROLETOKEN_EXPIRED', function () { + var resource = 'allow.testresgroup'; + var action = 'read'; + AuthZPEClient.allowAccess( + { + roleToken: expiredRoleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access denied due to expired RoleToken' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result DENY_ROLETOKEN_INVALID', function () { + var resource = 'athenz.test:nosuchresource'; + var action = 'read'; + AuthZPEClient.allowAccess( + { + roleToken: 'v=Z1;d=athenz.test;r=users', + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access denied due to invalid RoleToken' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result DENY_DOMAIN_MISMATCH', function () { + var resource = 'athenz.test:testresgroup.allow'; + var action = 'read'; + var roleTokenParams = { + version: 'Z1', + domain: 'athenz.test.nosuchdomain', + roles: ['users'], + }; + var rToken = new RoleToken(roleTokenParams); + rToken.sign(privateKeyK0); + var roleToken = rToken.getSignedToken(); + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access denied due to domain mismatch between Resource and RoleToken' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result DENY_DOMAIN_NOT_FOUND', function () { + var resource = 'athenz.test.nosuchdomain:testresgroup.allow'; + var action = 'read'; + var roleTokenParams = { + version: 'Z1', + domain: 'athenz.test.nosuchdomain', + roles: ['users'], + }; + var rToken = new RoleToken(roleTokenParams); + rToken.sign(privateKeyK0); + var roleToken = rToken.getSignedToken(); + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access denied due to domain not found in library cache' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result DENY_INVALID_PARAMETERS', function () { + var resource = ''; + var action = 'read'; + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access denied due to invalid/empty action/resource values' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result allow with asterisk (two asterisk match)', function () { + var resource = 'athenz.test:testresgroup'; + var action = 'XXXreadXXX'; + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access Check was explicitly allowed' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result allow with ? (question mark)', function () { + var resource = 'athenz.test:testresgroup.a'; + var action = 'get'; + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access Check was explicitly allowed' + ); + } + ); + }); + + it('should test AuthZPEClient allowAccess expecting result deny with ? (question mark)', function () { + var resource = 'athenz.test:testresgroup.toolong'; + var action = 'get'; + AuthZPEClient.allowAccess( + { + roleToken: roleToken, + resource: resource, + action: action, + }, + (err, accessCheckStatus) => { + expect(accessCheckStatus).to.deep.equal( + 'Access denied due to no match to any of the assertions defined in domain policy file' + ); + } + ); }); - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access Check was explicitly allowed'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result DENY', function() { - var resource = 'athenz.test:testresgroup.deny'; - var action = 'write'; - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access Check was explicitly denied'); - } - ); - - resource = 'athenz.test:testresgroup.deny'; - action = 'write'; - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access Check was explicitly denied'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result DENY_NO_MATCH', function() { - var resource = 'athenz.test:nosuchresource'; - var action = 'read'; - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access denied due to no match to any of the assertions defined in domain policy file'); - } - ); - - resource = 'allow.testresgroup'; - action = 'read'; - AuthZPEClient.allowAccess( - { - roleToken: noSuchRoleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access denied due to no match to any of the assertions defined in domain policy file'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result DENY_DOMAIN_EMPTY', function() { - var resource = 'allow.testresgroup'; - var action = 'read'; - AuthZPEClient.allowAccess( - { - roleToken: emptyRoleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access denied due to no policies in the domain file'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result DENY_ROLETOKEN_EXPIRED', function() { - var resource = 'allow.testresgroup'; - var action = 'read'; - AuthZPEClient.allowAccess( - { - roleToken: expiredRoleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access denied due to expired RoleToken'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result DENY_ROLETOKEN_INVALID', function() { - var resource = 'athenz.test:nosuchresource'; - var action = 'read'; - AuthZPEClient.allowAccess( - { - roleToken: 'v=Z1;d=athenz.test;r=users', - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access denied due to invalid RoleToken'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result DENY_DOMAIN_MISMATCH', function() { - var resource = 'athenz.test:testresgroup.allow'; - var action = 'read'; - var roleTokenParams = { - version: 'Z1', - domain: 'athenz.test.nosuchdomain', - roles: ['users'] - }; - var rToken = new RoleToken(roleTokenParams); - rToken.sign(privateKeyK0); - var roleToken = rToken.getSignedToken(); - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access denied due to domain mismatch between Resource and RoleToken'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result DENY_DOMAIN_NOT_FOUND', function() { - var resource = 'athenz.test.nosuchdomain:testresgroup.allow'; - var action = 'read'; - var roleTokenParams = { - version: 'Z1', - domain: 'athenz.test.nosuchdomain', - roles: ['users'] - }; - var rToken = new RoleToken(roleTokenParams); - rToken.sign(privateKeyK0); - var roleToken = rToken.getSignedToken(); - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access denied due to domain not found in library cache'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result DENY_INVALID_PARAMETERS', function() { - var resource = ''; - var action = 'read'; - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access denied due to invalid/empty action/resource values'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result allow with asterisk (two asterisk match)', function() { - var resource = 'athenz.test:testresgroup'; - var action = 'XXXreadXXX'; - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access Check was explicitly allowed'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result allow with ? (question mark)', function() { - var resource = 'athenz.test:testresgroup.a'; - var action = 'get'; - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access Check was explicitly allowed'); - } - ); - }); - - it('should test AuthZPEClient allowAccess expecting result deny with ? (question mark)', function() { - var resource = 'athenz.test:testresgroup.toolong'; - var action = 'get'; - AuthZPEClient.allowAccess( - { - roleToken: roleToken, - resource: resource, - action: action - }, - (err, accessCheckStatus) => { - expect(accessCheckStatus).to.deep.equal('Access denied due to no match to any of the assertions defined in domain policy file'); - } - ); - }); }); diff --git a/clients/nodejs/zpe/test/unit/PublicKeyStore.js b/clients/nodejs/zpe/test/unit/PublicKeyStore.js index 965edae2c04..1a34f8968b6 100644 --- a/clients/nodejs/zpe/test/unit/PublicKeyStore.js +++ b/clients/nodejs/zpe/test/unit/PublicKeyStore.js @@ -25,27 +25,7 @@ var sandbox; var policyDir = process.cwd() + '/test/resources/pol'; var confFileName = process.cwd() + '/test/resources/athenz.conf'; PublicKeyStore.setConfig({ - zpeClient: { - logLevel: 'debug', - policyDir: policyDir, - confFileName: confFileName, - tokenRefresh: 1800, - policyRefresh: 1800, - allowedOffset: 300, - disableCache: false, - updater: './ZPEUpdater', - disableWatch: true - } -}); - -describe('PublicKeyStore', function() { - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); - - afterEach(function() { - PublicKeyStore.setConfig({ - zpeClient: { + zpeClient: { logLevel: 'debug', policyDir: policyDir, confFileName: confFileName, @@ -54,86 +34,128 @@ describe('PublicKeyStore', function() { allowedOffset: 300, disableCache: false, updater: './ZPEUpdater', - disableWatch: true - } + disableWatch: true, + }, +}); + +describe('PublicKeyStore', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); }); - sandbox.restore(); - }); - it('should test PublicKeyStore constructor', function() { - var publicKeyStore = new PublicKeyStore(); - expect(publicKeyStore._zmsPublicKeyMap[0]).to.deep.equal(YBase64.ybase64Decode('LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--')); - expect(publicKeyStore._ztsPublicKeyMap[0]).to.deep.equal(YBase64.ybase64Decode('LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--')); - }); + afterEach(function () { + PublicKeyStore.setConfig({ + zpeClient: { + logLevel: 'debug', + policyDir: policyDir, + confFileName: confFileName, + tokenRefresh: 1800, + policyRefresh: 1800, + allowedOffset: 300, + disableCache: false, + updater: './ZPEUpdater', + disableWatch: true, + }, + }); + sandbox.restore(); + }); - it('should test PublicKeyStore fails', function() { - PublicKeyStore.setConfig({ - zpeClient: { - logLevel: 'debug', - policyDir: policyDir, - confFileName: null, - tokenRefresh: 1800, - policyRefresh: 1800, - allowedOffset: 300, - disableCache: false, - updater: './ZPEUpdater', - disableWatch: true - } + it('should test PublicKeyStore constructor', function () { + var publicKeyStore = new PublicKeyStore(); + expect(publicKeyStore._zmsPublicKeyMap[0]).to.deep.equal( + YBase64.ybase64Decode( + 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--' + ) + ); + expect(publicKeyStore._ztsPublicKeyMap[0]).to.deep.equal( + YBase64.ybase64Decode( + 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--' + ) + ); }); - var publicKeyStore; - expect(function() { - publicKeyStore = new PublicKeyStore(); - }).to.throw(Error, 'ENOENT: no such file or directory, open \'/home/athenz/conf/athenz/athenz.conf\''); - expect(publicKeyStore).to.be.undefined; - }); - it('should test PublicKeyStore fails with empty key', function() { - PublicKeyStore.setConfig({ - zpeClient: { - logLevel: 'debug', - policyDir: policyDir, - confFileName: process.cwd() + '/test/resources/athenz.empty.conf', - tokenRefresh: 1800, - policyRefresh: 1800, - allowedOffset: 300, - disableCache: false, - updater: './ZPEUpdater', - disableWatch: true - } + it('should test PublicKeyStore fails', function () { + PublicKeyStore.setConfig({ + zpeClient: { + logLevel: 'debug', + policyDir: policyDir, + confFileName: null, + tokenRefresh: 1800, + policyRefresh: 1800, + allowedOffset: 300, + disableCache: false, + updater: './ZPEUpdater', + disableWatch: true, + }, + }); + var publicKeyStore; + expect(function () { + publicKeyStore = new PublicKeyStore(); + }).to.throw( + Error, + "ENOENT: no such file or directory, open '/home/athenz/conf/athenz/athenz.conf'" + ); + expect(publicKeyStore).to.be.undefined; }); - var publicKeyStore = new PublicKeyStore(); - expect(publicKeyStore._zmsPublicKeyMap[0]).to.be.undefined; - expect(publicKeyStore._ztsPublicKeyMap[0]).to.be.undefined; - PublicKeyStore.setConfig({ - zpeClient: { - logLevel: 'debug', - policyDir: policyDir, - confFileName: process.cwd() + '/test/resources/athenz.empty.publickey.conf', - tokenRefresh: 1800, - policyRefresh: 1800, - allowedOffset: 300, - disableCache: false, - updater: './ZPEUpdater', - disableWatch: true - } + it('should test PublicKeyStore fails with empty key', function () { + PublicKeyStore.setConfig({ + zpeClient: { + logLevel: 'debug', + policyDir: policyDir, + confFileName: + process.cwd() + '/test/resources/athenz.empty.conf', + tokenRefresh: 1800, + policyRefresh: 1800, + allowedOffset: 300, + disableCache: false, + updater: './ZPEUpdater', + disableWatch: true, + }, + }); + var publicKeyStore = new PublicKeyStore(); + expect(publicKeyStore._zmsPublicKeyMap[0]).to.be.undefined; + expect(publicKeyStore._ztsPublicKeyMap[0]).to.be.undefined; + + PublicKeyStore.setConfig({ + zpeClient: { + logLevel: 'debug', + policyDir: policyDir, + confFileName: + process.cwd() + + '/test/resources/athenz.empty.publickey.conf', + tokenRefresh: 1800, + policyRefresh: 1800, + allowedOffset: 300, + disableCache: false, + updater: './ZPEUpdater', + disableWatch: true, + }, + }); + publicKeyStore = new PublicKeyStore(); + expect(publicKeyStore._zmsPublicKeyMap[0]).to.be.undefined; + expect(publicKeyStore._ztsPublicKeyMap[0]).to.be.undefined; }); - publicKeyStore = new PublicKeyStore(); - expect(publicKeyStore._zmsPublicKeyMap[0]).to.be.undefined; - expect(publicKeyStore._ztsPublicKeyMap[0]).to.be.undefined; - }); - it('should test PublicKeyStore getZtsKey', function() { - var publicKeyStore = new PublicKeyStore(); - expect(publicKeyStore.getZtsKey('0')).to.deep.equal(YBase64.ybase64Decode('LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--')); - expect(publicKeyStore.getZtsKey('1')).to.be.undefined; - expect(publicKeyStore.getZtsKey()).to.be.null; - }); + it('should test PublicKeyStore getZtsKey', function () { + var publicKeyStore = new PublicKeyStore(); + expect(publicKeyStore.getZtsKey('0')).to.deep.equal( + YBase64.ybase64Decode( + 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--' + ) + ); + expect(publicKeyStore.getZtsKey('1')).to.be.undefined; + expect(publicKeyStore.getZtsKey()).to.be.null; + }); - it('should test PublicKeyStore getZmsKey', function() { - var publicKeyStore = new PublicKeyStore(); - expect(publicKeyStore.getZmsKey('0')).to.deep.equal(YBase64.ybase64Decode('LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--')); - expect(publicKeyStore.getZmsKey('1')).to.be.undefined; - expect(publicKeyStore.getZmsKey()).to.be.null; - }); + it('should test PublicKeyStore getZmsKey', function () { + var publicKeyStore = new PublicKeyStore(); + expect(publicKeyStore.getZmsKey('0')).to.deep.equal( + YBase64.ybase64Decode( + 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFzdXBTOVJrUFQvMDdEcEVLUjZpRQp2Umh4S0NDQ0JlQUFnU3hJcHk3MUhqOHZkU21hNUdQTktpYUthczBOaVdpUXRCZW9OYTZia1NabE04bE9Sb1BwCjFzSy9uUjVCSWRwTjdWZ0NrWEdtSjY5THJmYm44ODdHWjBPV0tFZFlKcXI0S2tpMktHOFFzYTgxNXE4ei9LRk4KRjg4NjlqYzRRbDdkVnY3NUZDays4SXNJcnBCZ3I1eU1RTnZNb24xYmY4MUZka0lJdE9iUnZIMm9NeHZLQVRqVworRUpvcytTbENITmQreDR1WUM5bE40SGk2cXFKTWNBZ3I0aTRhM0JNV2pHSk1DclY3UWpibzcwOW1jS2JqUE9JCmwvMEs3YjczZ2ZhSEZCZnBmaXFlanNsa2xab3VoSUhvdWxoZk93dXdSZStmMW14UkpsWmlhMjh6K29VZEVYSVUKZlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--' + ) + ); + expect(publicKeyStore.getZmsKey('1')).to.be.undefined; + expect(publicKeyStore.getZmsKey()).to.be.null; + }); }); diff --git a/clients/nodejs/zpe/test/unit/ZPEMatch.js b/clients/nodejs/zpe/test/unit/ZPEMatch.js index 190d7104e37..0fec6848d3d 100644 --- a/clients/nodejs/zpe/test/unit/ZPEMatch.js +++ b/clients/nodejs/zpe/test/unit/ZPEMatch.js @@ -27,11 +27,11 @@ var rdlMock = require('../config/RdlMock'); var sandbox; var params = { - zts: 'zts.athenz.com', - domainName: null, - serviceName: null, - siaProvider: null, - identity: null + zts: 'zts.athenz.com', + domainName: null, + serviceName: null, + siaProvider: null, + identity: null, }; var domainName = 'athenz.user'; @@ -41,174 +41,173 @@ var siaMock = new siaProviderMock(domainName, serviceName); var ideMock = new identityMock(domainName, serviceName); var siaParams = { - domainName: domainName, - serviceName: serviceName, - siaProvider: siaMock, - identity: null + domainName: domainName, + serviceName: serviceName, + siaProvider: siaMock, + identity: null, }; var cacheKey = 'cacheKeyRoleToken'; -describe('ZPEMatch completely match', function() { - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); - - afterEach(function() { - sandbox.restore(); - }); - - it('should test ZPEMatch equalMatches', function() { - var zpeMatch = ZPEMatch('athenz.test.aaa'); - expect(zpeMatch.equal.matches('athenz.test.aaa')).to.equal(true); - zpeMatch = ZPEMatch('athenz.test.aaa'); - expect(zpeMatch.equal.matches('athenz.test.bbb')).to.equal(false); - }); -}); +describe('ZPEMatch completely match', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + afterEach(function () { + sandbox.restore(); + }); -describe('ZPEMatch check * (Any charactor wildcard option)', function() { - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); - - afterEach(function() { - sandbox.restore(); - }); - - it("startswith 'aaa*' matches 'aaabbb'", function() { - var zpeMatch = ZPEMatch('aaa*'); - expect(zpeMatch.startswith.matches('aaabbb')).to.equal(true); - }); - - it("startswith 'bbb*' un-matches 'aaabbb'", function() { - var zpeMatch = ZPEMatch('bbb*'); - expect(zpeMatch.startswith.matches('aaabbb')).to.equal(false); - }); - - it("regex '*bbb' matches 'aaabbb'", function() { - var zpeMatch = ZPEMatch('*bbb'); - expect(zpeMatch.regex.matches('aaabbb')).to.equal(true); - }); - - it("regex '*bbb' un-matches 'bbbaaa'", function() { - var zpeMatch = ZPEMatch('*bbb'); - expect(zpeMatch.regex.matches('bbbaaa')).to.equal(false); - }); - - it("all '*' matches ''(empty string)", function() { - var zpeMatch = ZPEMatch('*'); - expect(zpeMatch.all.matches('')).to.equal(true); - }); - - it("all '*' matches 'thisistest'", function() { - var zpeMatch = ZPEMatch('*'); - expect(zpeMatch.all.matches('thisistest')).to.equal(true); - }); - - it("regex 'aaa*bbb' matches 'aaaxxxbbb'", function() { - var zpeMatch = ZPEMatch('aaa*bbb'); - expect(zpeMatch.regex.matches('aaaxxxbbb')).to.equal(true); - }); - - it("regex 'aaa*bbb' un-matches 'aaxxxbbb'", function() { - var zpeMatch = ZPEMatch('aaa*bbb'); - expect(zpeMatch.regex.matches('aaxxxbbb')).to.equal(false); - }); + it('should test ZPEMatch equalMatches', function () { + var zpeMatch = ZPEMatch('athenz.test.aaa'); + expect(zpeMatch.equal.matches('athenz.test.aaa')).to.equal(true); + zpeMatch = ZPEMatch('athenz.test.aaa'); + expect(zpeMatch.equal.matches('athenz.test.bbb')).to.equal(false); + }); }); -describe('ZPEMatch check ? (Single charactor wildcard option)', function() { - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); - - afterEach(function() { - sandbox.restore(); - }); - - it("regex 'aaa?' matches 'aaab'", function() { - var zpeMatch = ZPEMatch('aaa?'); - expect(zpeMatch.regex.matches('aaab')).to.equal(true); - }); - - it("regex 'bbb?' un-matches 'abbb'", function() { - var zpeMatch = ZPEMatch('bbb?'); - expect(zpeMatch.regex.matches('abbb')).to.equal(false); - }); - - it("regex '?bbb' matches 'abbb'", function() { - var zpeMatch = ZPEMatch('?bbb'); - expect(zpeMatch.regex.matches('abbb')).to.equal(true); - }); - - it("regex '?bbb' un-matches 'bbba'", function() { - var zpeMatch = ZPEMatch('?bbb'); - expect(zpeMatch.regex.matches('bbba')).to.equal(false); - }); - - it("regex '?' un-matches ''(empty string)", function() { - var zpeMatch = ZPEMatch('?'); - expect(zpeMatch.regex.matches('')).to.equal(false); - }); - - it("regex 'aaa?bbb' matches 'aaaxbbb'", function() { - var zpeMatch = ZPEMatch('aaa?bbb'); - expect(zpeMatch.regex.matches('aaaxbbb')).to.equal(true); - }); - - it("regex 'aaa?bbb' un-matches 'aaxbbb'", function() { - var zpeMatch = ZPEMatch('aaa?bbb'); - expect(zpeMatch.regex.matches('aaxbbb')).to.equal(false); - }); +describe('ZPEMatch check * (Any charactor wildcard option)', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it("startswith 'aaa*' matches 'aaabbb'", function () { + var zpeMatch = ZPEMatch('aaa*'); + expect(zpeMatch.startswith.matches('aaabbb')).to.equal(true); + }); + + it("startswith 'bbb*' un-matches 'aaabbb'", function () { + var zpeMatch = ZPEMatch('bbb*'); + expect(zpeMatch.startswith.matches('aaabbb')).to.equal(false); + }); + + it("regex '*bbb' matches 'aaabbb'", function () { + var zpeMatch = ZPEMatch('*bbb'); + expect(zpeMatch.regex.matches('aaabbb')).to.equal(true); + }); + + it("regex '*bbb' un-matches 'bbbaaa'", function () { + var zpeMatch = ZPEMatch('*bbb'); + expect(zpeMatch.regex.matches('bbbaaa')).to.equal(false); + }); + + it("all '*' matches ''(empty string)", function () { + var zpeMatch = ZPEMatch('*'); + expect(zpeMatch.all.matches('')).to.equal(true); + }); + + it("all '*' matches 'thisistest'", function () { + var zpeMatch = ZPEMatch('*'); + expect(zpeMatch.all.matches('thisistest')).to.equal(true); + }); + + it("regex 'aaa*bbb' matches 'aaaxxxbbb'", function () { + var zpeMatch = ZPEMatch('aaa*bbb'); + expect(zpeMatch.regex.matches('aaaxxxbbb')).to.equal(true); + }); + + it("regex 'aaa*bbb' un-matches 'aaxxxbbb'", function () { + var zpeMatch = ZPEMatch('aaa*bbb'); + expect(zpeMatch.regex.matches('aaxxxbbb')).to.equal(false); + }); }); -describe('ZPEMatch check regex meta symbol', function() { - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); - - afterEach(function() { - sandbox.restore(); - }); - - it("regex 'aaa.' un-matches 'aaaa'", function() { - var zpeMatch = ZPEMatch('aaa.'); - expect(zpeMatch.regex.matches('aaaa')).to.equal(false); - }); - - const metaSymbols = ['\\', '^', '$', '.', '|', '[', '+', '(', ')', '{']; - metaSymbols.forEach((meta) => { - it(`check meta symbol ${meta} is escaped`, function() { - var zpeMatch = null; - metaSymbols.forEach((meta) => { - zpeMatch = ZPEMatch(`aaa${meta}`); - expect(zpeMatch.regex.matches(`aaa${meta}`)).to.equal(true); - }); - }); - }); +describe('ZPEMatch check ? (Single charactor wildcard option)', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it("regex 'aaa?' matches 'aaab'", function () { + var zpeMatch = ZPEMatch('aaa?'); + expect(zpeMatch.regex.matches('aaab')).to.equal(true); + }); + + it("regex 'bbb?' un-matches 'abbb'", function () { + var zpeMatch = ZPEMatch('bbb?'); + expect(zpeMatch.regex.matches('abbb')).to.equal(false); + }); + + it("regex '?bbb' matches 'abbb'", function () { + var zpeMatch = ZPEMatch('?bbb'); + expect(zpeMatch.regex.matches('abbb')).to.equal(true); + }); + + it("regex '?bbb' un-matches 'bbba'", function () { + var zpeMatch = ZPEMatch('?bbb'); + expect(zpeMatch.regex.matches('bbba')).to.equal(false); + }); + + it("regex '?' un-matches ''(empty string)", function () { + var zpeMatch = ZPEMatch('?'); + expect(zpeMatch.regex.matches('')).to.equal(false); + }); + + it("regex 'aaa?bbb' matches 'aaaxbbb'", function () { + var zpeMatch = ZPEMatch('aaa?bbb'); + expect(zpeMatch.regex.matches('aaaxbbb')).to.equal(true); + }); + + it("regex 'aaa?bbb' un-matches 'aaxbbb'", function () { + var zpeMatch = ZPEMatch('aaa?bbb'); + expect(zpeMatch.regex.matches('aaxbbb')).to.equal(false); + }); }); -describe("ZPEMatch adds implicitly '^' and '$' to enable strict matches", function() { - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); +describe('ZPEMatch check regex meta symbol', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); - afterEach(function() { - sandbox.restore(); - }); + afterEach(function () { + sandbox.restore(); + }); - it("regex 'a?b' matches 'aab'", function() { - var zpeMatch = ZPEMatch('a?b'); - expect(zpeMatch.regex.matches('aab')).to.equal(true); - }); + it("regex 'aaa.' un-matches 'aaaa'", function () { + var zpeMatch = ZPEMatch('aaa.'); + expect(zpeMatch.regex.matches('aaaa')).to.equal(false); + }); - it("regex 'a?b' un-matches 'aaab'", function() { - var zpeMatch = ZPEMatch('a?b'); - expect(zpeMatch.regex.matches('aaab')).to.equal(false); - }); + const metaSymbols = ['\\', '^', '$', '.', '|', '[', '+', '(', ')', '{']; + metaSymbols.forEach((meta) => { + it(`check meta symbol ${meta} is escaped`, function () { + var zpeMatch = null; + metaSymbols.forEach((meta) => { + zpeMatch = ZPEMatch(`aaa${meta}`); + expect(zpeMatch.regex.matches(`aaa${meta}`)).to.equal(true); + }); + }); + }); +}); - it("regex 'a?b' un-matches 'abbb'", function() { - var zpeMatch = ZPEMatch('a?b'); - expect(zpeMatch.regex.matches('abbb')).to.equal(false); - }); +describe("ZPEMatch adds implicitly '^' and '$' to enable strict matches", function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + afterEach(function () { + sandbox.restore(); + }); + + it("regex 'a?b' matches 'aab'", function () { + var zpeMatch = ZPEMatch('a?b'); + expect(zpeMatch.regex.matches('aab')).to.equal(true); + }); + + it("regex 'a?b' un-matches 'aaab'", function () { + var zpeMatch = ZPEMatch('a?b'); + expect(zpeMatch.regex.matches('aaab')).to.equal(false); + }); + + it("regex 'a?b' un-matches 'abbb'", function () { + var zpeMatch = ZPEMatch('a?b'); + expect(zpeMatch.regex.matches('abbb')).to.equal(false); + }); }); diff --git a/clients/nodejs/zpe/test/unit/ZPEUpdater.js b/clients/nodejs/zpe/test/unit/ZPEUpdater.js index 8ca5249b9c4..c84a218013c 100644 --- a/clients/nodejs/zpe/test/unit/ZPEUpdater.js +++ b/clients/nodejs/zpe/test/unit/ZPEUpdater.js @@ -21,15 +21,14 @@ var cache = require('memory-cache'); var sandbox; -describe('ZPEUpdater', function() { - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); +describe('ZPEUpdater', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); - afterEach(function() { - sandbox.restore(); - }); + afterEach(function () { + sandbox.restore(); + }); - it('should test ZPEUpdater', function() { - }); + it('should test ZPEUpdater', function () {}); }); diff --git a/clients/nodejs/zpe/test/unit/index.js b/clients/nodejs/zpe/test/unit/index.js index 41df3ca0ef7..5ab09feee3c 100644 --- a/clients/nodejs/zpe/test/unit/index.js +++ b/clients/nodejs/zpe/test/unit/index.js @@ -27,11 +27,11 @@ var rdlMock = require('../config/RdlMock'); var sandbox; var params = { - zts: 'zts.athenz.com', - domainName: null, - serviceName: null, - siaProvider: null, - identity: null + zts: 'zts.athenz.com', + domainName: null, + serviceName: null, + siaProvider: null, + identity: null, }; var domainName = 'athenz.user'; @@ -41,23 +41,22 @@ var siaMock = new siaProviderMock(domainName, serviceName); var ideMock = new identityMock(domainName, serviceName); var siaParams = { - domainName: domainName, - serviceName: serviceName, - siaProvider: siaMock, - identity: null + domainName: domainName, + serviceName: serviceName, + siaProvider: siaMock, + identity: null, }; var cacheKey = 'cacheKeyRoleToken'; -describe('zpe_nodejs_client', function() { - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); +describe('zpe_nodejs_client', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); - afterEach(function() { - sandbox.restore(); - }); + afterEach(function () { + sandbox.restore(); + }); - it('should test zpe_nodejs_client', function() { - }); + it('should test zpe_nodejs_client', function () {}); }); diff --git a/clients/nodejs/zts/.prettierrc b/clients/nodejs/zts/.prettierrc new file mode 100644 index 00000000000..6bb22e39328 --- /dev/null +++ b/clients/nodejs/zts/.prettierrc @@ -0,0 +1,10 @@ +arrowParens: always +bracketSpacing: true +endOfLine: lf +jsxBracketSameLine: false +jsxSingleQuote: true +semi: true +singleQuote: true +tabWidth: 4 +trailingComma: es5 +useTabs: false diff --git a/clients/nodejs/zts/Gruntfile.js b/clients/nodejs/zts/Gruntfile.js deleted file mode 100644 index 51e37542589..00000000000 --- a/clients/nodejs/zts/Gruntfile.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*jshint camelcase: false*/ -'use strict'; - -//var config = require('./config/config.js')(); - -module.exports = function(grunt) { - require('load-grunt-tasks')(grunt); - var path = require('path'), - pretty = require('prettysize'); - - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - copy: {}, - clean: ['build/', 'artifacts/'], - browserify: {}, - eslint: { - options: { - cache: true, - cacheFile: '.eslintcodecache', - configFile: 'eslint.json' - }, - target: ['src/**/*.js', './*.js', 'test/**/*.js'] - }, - filesize: {}, - jshint: { - options: { - globalstrict: true, - expr: true, - esversion: 6, - globals: { - "expect": true, - "assert": false, - "it": false, - "require": false, - "describe": false, - "beforeEach": false, - "afterEach": false, - "before": false, - "after": false, - "$": false, - "$$": false, - "browser": false, - "Buffer": false, - "module": false, - "global": false, - "exports": false, - "process": false, - "console": false, - "__dirname": false, - "Intl": false, - } - }, - files: ['Gruntfile.js', 'src/**/*.js', './*.js', 'test/**/*.js'], - } - }); - - grunt.registerTask('test', 'Run tests', function(){ - grunt.log.ok('Running unit tests with node@' + process.version); - var done = this.async(); - grunt.util.spawn({ - cmd: path.join(__dirname, 'node_modules/nyc/bin/nyc.js'), - args: ['node_modules/.bin/jenkins-mocha', '--require', 'test/config/mock.js', 'test/unit/**/*.js'], - opts: { stdio: 'inherit' } - }, done); - }); - - grunt.registerTask('lint', function() { - grunt.task.run(['jshint', 'eslint']); - }); - - grunt.registerTask('build', function() { - grunt.task.run(['clean']); - }); - - grunt.registerTask('build-dev', function() { - grunt.task.run(['clean']); - }); - - grunt.registerTask('default', ['lint', 'build', 'test']); - - grunt.registerTask('local', ['build-dev']); -}; diff --git a/clients/nodejs/zts/Makefile b/clients/nodejs/zts/Makefile index 73e1ea15fbc..93def37476f 100644 --- a/clients/nodejs/zts/Makefile +++ b/clients/nodejs/zts/Makefile @@ -11,10 +11,8 @@ NPM := $(shell command -v npm 2> /dev/null) ifdef NPM -all: - npm install - $(PWD)/node_modules/grunt-cli/bin/grunt - +.PHONY: build lint test +all: build lint test else all: @@ -22,5 +20,17 @@ all: endif +build: + @echo "Make: Build" + npm install + +lint: + @echo "Make: lint" + npm run ci-lint + +test: + @echo "Make: unit tests" + npm run test + clean: - rm -rf build node_modules artifacts + rm -rf node_modules artifacts diff --git a/clients/nodejs/zts/eslint.json b/clients/nodejs/zts/eslint.json deleted file mode 100644 index b4c9af40abc..00000000000 --- a/clients/nodejs/zts/eslint.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "env": { - "es6": true - }, - "rules": { - "eqeqeq": "warn", - "curly": "warn", - "no-undef": "off", - "no-undefined": "off", - "no-unused-vars": "off", - "max-params": ["error", {"max":5}], - "complexity": ["error", {"max":8}] - } -} diff --git a/clients/nodejs/zts/index.js b/clients/nodejs/zts/index.js index 58158b9d492..922442dde12 100644 --- a/clients/nodejs/zts/index.js +++ b/clients/nodejs/zts/index.js @@ -21,263 +21,334 @@ let cacheDisabled = false; let tokenMinExpiryTime = 900; class ZTSClient { - constructor(params) { - winston.level = config.logLevel; - - this._ztsUrl = null; - this._domain = null; - this._service = null; - this._ztsClient = null; - this._ztsClientFactory = null; - this._siaProvider = null; - - this._enablePrefetch = true; - this.principal = null; - this._cache = require('memory-cache'); - - this._initClient(params); - } - - static setConfig(c) { - config = Object.assign({}, config, c.ztsClient); - } - - static _initConfigValues() { - /* The minimum token expiry time by default is 15 minutes (900). By default the - * server gives out role tokens for 2 hours and with this setting we'll be able - * to cache tokens for 1hr45mins before requesting a new one from ZTS */ - if (config.tokenMinExpiryTime >= 0) { - tokenMinExpiryTime = config.tokenMinExpiryTime; - } else { - tokenMinExpiryTime = 900; - } + constructor(params) { + winston.level = config.logLevel; - if (config.disableCache) { - cacheDisabled = config.disableCache; - } else { - cacheDisabled = false; - } + this._ztsUrl = null; + this._domain = null; + this._service = null; + this._ztsClient = null; + this._ztsClientFactory = null; + this._siaProvider = null; - return true; - } - - /*eslint complexity: ["error", 11]*/ - _initClient(params) { - if (params.domainName || params.serviceName || params.siaProvider) { - if(!params.domainName || !params.serviceName || !params.siaProvider) { - throw new Error('domainName & serviceName & siaProvider must be specified.'); - } - this._domain = params.domainName; - this._service = params.serviceName; - this._siaProvider = params.siaProvider; - } else if (params.identity) { - if (!params.identity.getAuthority()) { - throw new Error('Principal Authority cannot be null'); - } - this.principal = params.identity; - this._enablePrefetch = false; // can't use this domain and service for prefetch - } else { - this._enablePrefetch = false; // can't use this domain and service for prefetch - } + this._enablePrefetch = true; + this.principal = null; + this._cache = require('memory-cache'); - if (params.zts) { - this._ztsUrl = params.zts; - } else { - this._ztsUrl = config.zts; + this._initClient(params); } - this._ztsClientFactory = require('./libs/rdl-rest')({ - apiHost: this._ztsUrl, - rdl: require('./config/zts.json'), - requestOpts: { - strictSSL: config.strictSSL - } - }); - - if (this.principal) { - this._domain = this.principal.getDomain(); - this._service = this.principal.getName(); - this._ztsClient = this._ztsClientFactory(null, { - [this.principal.getAuthority().getHeader()]: this.principal.getCredentials() - }); + static setConfig(c) { + config = Object.assign({}, config, c.ztsClient); } - } - _addPrincipalCredentials(identity, resetServiceDetails) { - if (identity && identity.getAuthority()) { - this._ztsClient = this._ztsClientFactory(null, { - [identity.getAuthority().getHeader()]: identity.getCredentials() - }); - } + static _initConfigValues() { + /* The minimum token expiry time by default is 15 minutes (900). By default the + * server gives out role tokens for 2 hours and with this setting we'll be able + * to cache tokens for 1hr45mins before requesting a new one from ZTS */ + if (config.tokenMinExpiryTime >= 0) { + tokenMinExpiryTime = config.tokenMinExpiryTime; + } else { + tokenMinExpiryTime = 900; + } - // if the client is adding new principal identity then we have to - // clear out the sia provider object reference so that we don't try - // to get a service token since we already have one given to us - if (resetServiceDetails) { - this._siaProvider = null; + if (config.disableCache) { + cacheDisabled = config.disableCache; + } else { + cacheDisabled = false; + } + + return true; } - this.principal = identity; - return this; - } + /*eslint complexity: ["error", 11]*/ + _initClient(params) { + if (params.domainName || params.serviceName || params.siaProvider) { + if ( + !params.domainName || + !params.serviceName || + !params.siaProvider + ) { + throw new Error( + 'domainName & serviceName & siaProvider must be specified.' + ); + } + this._domain = params.domainName; + this._service = params.serviceName; + this._siaProvider = params.siaProvider; + } else if (params.identity) { + if (!params.identity.getAuthority()) { + throw new Error('Principal Authority cannot be null'); + } + this.principal = params.identity; + this._enablePrefetch = false; // can't use this domain and service for prefetch + } else { + this._enablePrefetch = false; // can't use this domain and service for prefetch + } - _sameCredentialsAsBefore(svcPrincipal) { - // if we don't have a principal or no credentials - // then the principal has changed - if (!this.principal) { - return false; - } + if (params.zts) { + this._ztsUrl = params.zts; + } else { + this._ztsUrl = config.zts; + } - const creds = this.principal.getCredentials(); - if (!creds) { - return false; + this._ztsClientFactory = require('./libs/rdl-rest')({ + apiHost: this._ztsUrl, + rdl: require('./config/zts.json'), + requestOpts: { + strictSSL: config.strictSSL, + }, + }); + + if (this.principal) { + this._domain = this.principal.getDomain(); + this._service = this.principal.getName(); + this._ztsClient = this._ztsClientFactory(null, { + [this.principal.getAuthority().getHeader()]: + this.principal.getCredentials(), + }); + } } - return creds === svcPrincipal.getCredentials(); - } + _addPrincipalCredentials(identity, resetServiceDetails) { + if (identity && identity.getAuthority()) { + this._ztsClient = this._ztsClientFactory(null, { + [identity.getAuthority().getHeader()]: + identity.getCredentials(), + }); + } - _updateServicePrincipal() { - /* if we have a service principal then we need to keep updating - * our PrincipalToken otherwise it might expire. */ - if (!this._siaProvider) { - return false; - } + // if the client is adding new principal identity then we have to + // clear out the sia provider object reference so that we don't try + // to get a service token since we already have one given to us + if (resetServiceDetails) { + this._siaProvider = null; + } - const svcPrincipal = this._siaProvider.getIdentity(this._domain, this._service); - - // if we get no principal from our sia provider, then we - // should log and throw an IllegalArgumentException otherwise the - // client doesn't know that something bad has happened - in this - // case illegal domain/service was passed to the constructor - // and the ZTS Server just rejects the request with 401 - if (!svcPrincipal) { - const msg = 'UpdateServicePrincipal: Unable to get PrincipalToken ' + - 'from SIA Provider for ' + this._domain + '.' + this._service; - winston.error(msg); - throw new Error(msg); + this.principal = identity; + return this; } - // if the principal has the same credentials as before - // then we don't need to update anything - if (this._sameCredentialsAsBefore(svcPrincipal)) { - return false; - } + _sameCredentialsAsBefore(svcPrincipal) { + // if we don't have a principal or no credentials + // then the principal has changed + if (!this.principal) { + return false; + } - this._addPrincipalCredentials(svcPrincipal, false); - return true; - } - - getRoleToken(params, cb) { - const pars = Object.assign({ - domainName: null, - roleName: null, - minExpiryTime: null, - maxExpiryTime: null, - ignoreCache: false, - proxyForPrincipal: null - }, params); - const that = this; - - if(!pars.domainName) { - return cb(new Error('GetRoleToken: domainName must not be null.'), null); + const creds = this.principal.getCredentials(); + if (!creds) { + return false; + } + + return creds === svcPrincipal.getCredentials(); } - let roleToken = null; - - // first lookup in our cache to see if it can be satisfied - // only if we're not asked to ignore the cache - let cacheKey = null; - if (!cacheDisabled) { - cacheKey = this._getRoleTokenCacheKeySetTenant(pars.domainName, pars.roleName, pars.proxyForPrincipal); - if (cacheKey && !params.ignoreCache) { - roleToken = this._lookupRoleTokenInCache(cacheKey, pars.minExpiryTime, pars.maxExpiryTime); - if (roleToken) { - return cb(null, roleToken); + _updateServicePrincipal() { + /* if we have a service principal then we need to keep updating + * our PrincipalToken otherwise it might expire. */ + if (!this._siaProvider) { + return false; } - } - } - // if no hit then we need to request a new token from ZTS - this._updateServicePrincipal(); - this._ztsClient.getRoleToken(pars, function(err, res) { - if (err) { - return cb(err, null); - } - // need to add the token to our cache. If our principal was - // updated then we need to retrieve a new cache key - - if (!cacheDisabled) { - if (cacheKey) { - that._cache.put(cacheKey, res, config.tokenRefresh * 1000); + const svcPrincipal = this._siaProvider.getIdentity( + this._domain, + this._service + ); + + // if we get no principal from our sia provider, then we + // should log and throw an IllegalArgumentException otherwise the + // client doesn't know that something bad has happened - in this + // case illegal domain/service was passed to the constructor + // and the ZTS Server just rejects the request with 401 + if (!svcPrincipal) { + const msg = + 'UpdateServicePrincipal: Unable to get PrincipalToken ' + + 'from SIA Provider for ' + + this._domain + + '.' + + this._service; + winston.error(msg); + throw new Error(msg); } - } - return cb(null, res); - }); - } - - _getRoleTokenCacheKeySetTenant(domainName, roleName, proxyForPrincipal) { - return this._getRoleTokenCacheKey(this._domain, this._service, domainName, roleName, proxyForPrincipal); - } - - _getRoleTokenCacheKey(tenantDomain, tenantService, domainName, roleName, proxyForPrincipal) { - if (!tenantDomain) { - return null; - } - let cacheKey = 'p=' + tenantDomain; - if (tenantService) { - cacheKey += '.' + tenantService; - } - cacheKey += ';d=' + domainName; - if (roleName) { - cacheKey += ';r=' + roleName; - } - if (proxyForPrincipal) { - cacheKey += ';u=' + proxyForPrincipal; + // if the principal has the same credentials as before + // then we don't need to update anything + if (this._sameCredentialsAsBefore(svcPrincipal)) { + return false; + } + + this._addPrincipalCredentials(svcPrincipal, false); + return true; } - return cacheKey; - } + getRoleToken(params, cb) { + const pars = Object.assign( + { + domainName: null, + roleName: null, + minExpiryTime: null, + maxExpiryTime: null, + ignoreCache: false, + proxyForPrincipal: null, + }, + params + ); + const that = this; + + if (!pars.domainName) { + return cb( + new Error('GetRoleToken: domainName must not be null.'), + null + ); + } + + let roleToken = null; + + // first lookup in our cache to see if it can be satisfied + // only if we're not asked to ignore the cache + let cacheKey = null; + if (!cacheDisabled) { + cacheKey = this._getRoleTokenCacheKeySetTenant( + pars.domainName, + pars.roleName, + pars.proxyForPrincipal + ); + if (cacheKey && !params.ignoreCache) { + roleToken = this._lookupRoleTokenInCache( + cacheKey, + pars.minExpiryTime, + pars.maxExpiryTime + ); + if (roleToken) { + return cb(null, roleToken); + } + } + } - _isExpiredToken(expiryTime, minExpiryTime, maxExpiryTime, tokenMinExpiryTime) { - // we'll first make sure if we're given both min and max expiry - // times then both conditions are satisfied - if (minExpiryTime && expiryTime < minExpiryTime) { - return true; + // if no hit then we need to request a new token from ZTS + this._updateServicePrincipal(); + this._ztsClient.getRoleToken(pars, function (err, res) { + if (err) { + return cb(err, null); + } + // need to add the token to our cache. If our principal was + // updated then we need to retrieve a new cache key + + if (!cacheDisabled) { + if (cacheKey) { + that._cache.put(cacheKey, res, config.tokenRefresh * 1000); + } + } + return cb(null, res); + }); } - if (maxExpiryTime && expiryTime > maxExpiryTime) { - return true; + _getRoleTokenCacheKeySetTenant(domainName, roleName, proxyForPrincipal) { + return this._getRoleTokenCacheKey( + this._domain, + this._service, + domainName, + roleName, + proxyForPrincipal + ); } - // if both limits were null then we need to make sure - // that our token is valid for based on our min configured value - if (!minExpiryTime && !maxExpiryTime && expiryTime < tokenMinExpiryTime) { - return true; + _getRoleTokenCacheKey( + tenantDomain, + tenantService, + domainName, + roleName, + proxyForPrincipal + ) { + if (!tenantDomain) { + return null; + } + let cacheKey = 'p=' + tenantDomain; + if (tenantService) { + cacheKey += '.' + tenantService; + } + cacheKey += ';d=' + domainName; + + if (roleName) { + cacheKey += ';r=' + roleName; + } + if (proxyForPrincipal) { + cacheKey += ';u=' + proxyForPrincipal; + } + + return cacheKey; } - return false; - } + _isExpiredToken( + expiryTime, + minExpiryTime, + maxExpiryTime, + tokenMinExpiryTime + ) { + // we'll first make sure if we're given both min and max expiry + // times then both conditions are satisfied + if (minExpiryTime && expiryTime < minExpiryTime) { + return true; + } + + if (maxExpiryTime && expiryTime > maxExpiryTime) { + return true; + } + + // if both limits were null then we need to make sure + // that our token is valid for based on our min configured value + if ( + !minExpiryTime && + !maxExpiryTime && + expiryTime < tokenMinExpiryTime + ) { + return true; + } - _lookupRoleTokenInCache(cacheKey, minExpiryTime, maxExpiryTime) { - const roleToken = this._cache.get(cacheKey); - if (!roleToken) { - winston.info('LookupRoleTokenInCache: cache-lookup key: ' + cacheKey + ' result: not found'); - return null; + return false; } - // before returning our cache hit we need to make sure it - // satisfies the time requirements as specified by the client - const expiryTime = roleToken.expiryTime - Math.floor(Date.now() / 1000); - if (this._isExpiredToken(expiryTime, minExpiryTime, maxExpiryTime, tokenMinExpiryTime)) { - winston.info('LookupRoleTokenInCache: role-cache-lookup key: ' + cacheKey + ' token-expiry: ' + expiryTime + - ' req-min-expiry: ' + this.minExpiryTime + ' req-max-expiry: ' + this.maxExpiryTime + - ' client-min-expiry: ' + tokenMinExpiryTime + ' result: expired'); - this._cache.del(cacheKey); - return null; + _lookupRoleTokenInCache(cacheKey, minExpiryTime, maxExpiryTime) { + const roleToken = this._cache.get(cacheKey); + if (!roleToken) { + winston.info( + 'LookupRoleTokenInCache: cache-lookup key: ' + + cacheKey + + ' result: not found' + ); + return null; + } + + // before returning our cache hit we need to make sure it + // satisfies the time requirements as specified by the client + const expiryTime = roleToken.expiryTime - Math.floor(Date.now() / 1000); + if ( + this._isExpiredToken( + expiryTime, + minExpiryTime, + maxExpiryTime, + tokenMinExpiryTime + ) + ) { + winston.info( + 'LookupRoleTokenInCache: role-cache-lookup key: ' + + cacheKey + + ' token-expiry: ' + + expiryTime + + ' req-min-expiry: ' + + this.minExpiryTime + + ' req-max-expiry: ' + + this.maxExpiryTime + + ' client-min-expiry: ' + + tokenMinExpiryTime + + ' result: expired' + ); + this._cache.del(cacheKey); + return null; + } + return roleToken; } - return roleToken; - } } ZTSClient._initConfigValues(); diff --git a/clients/nodejs/zts/package.json b/clients/nodejs/zts/package.json index d78b6d7ae48..47f5c78f9b9 100644 --- a/clients/nodejs/zts/package.json +++ b/clients/nodejs/zts/package.json @@ -11,10 +11,10 @@ }, "scripts": { "build": "make", - "lint": "grunt lint", + "fix-lint": "prettier --write ./*.js test/**/*.js", + "ci-lint": "prettier --list-different ./*.js test/**/*.js", "test": "./node_modules/nyc/bin/nyc.js node_modules/.bin/jenkins-mocha --require test/config/mock.js test/unit/*.js", "testDev": "mocha --require test/config/mock.js test/unit/*.js", - "functional": "grunt functional-sd", "noop": "echo Ignoring this section." }, "keywords": [ @@ -36,29 +36,16 @@ "winston": "^2.3.1" }, "devDependencies": { + "prettier": "^2.3.0", "chai": "~3.5.0", "chai-as-promised": "~6.0.0", "chart.js": "^2.2.1", "csslint": "^1.0.5", "jshint": "^2.9.4", - "grunt": "~0.4.5", - "grunt-contrib-jshint": "^1.1.0", - "grunt-browserify": "~5.0.0", - "grunt-cli": "~1.2.0", - "grunt-contrib-clean": "~0.7.0", - "grunt-contrib-copy": "~1.0.0", - "grunt-contrib-watch": "^1.0.0", - "grunt-eslint": "^19.0.0", - "grunt-exec": "~1.0.1", - "grunt-filesize": "0.0.7", - "grunt-postcss": "^0.8.0", - "grunt-protractor-webdriver": "~0.2.5", - "grunt-webdriver": "~1.0.0", "nyc": "^11.0.3", "jenkins-mocha": "^5.0.0", "jquery": "^3.1.1", "jquery-ui": "^1.10.5", - "load-grunt-tasks": "^3.5.2", "minifyify": "~7.3.5", "mkdirp": "~0.5.1", "mocha": "~3.2.0", diff --git a/clients/nodejs/zts/test/config/IdentityMock.js b/clients/nodejs/zts/test/config/IdentityMock.js index edcc3d9e234..11215fb0d26 100644 --- a/clients/nodejs/zts/test/config/IdentityMock.js +++ b/clients/nodejs/zts/test/config/IdentityMock.js @@ -14,38 +14,38 @@ 'use strict'; class IdentityMock { - constructor(domain, name) { - this._domain = domain; - this._name = name; - - this._creds = null; - if(domain && name) { - this._creds = 'v=S1;d=' + domain + ';n=' + name + ';'; + constructor(domain, name) { + this._domain = domain; + this._name = name; + + this._creds = null; + if (domain && name) { + this._creds = 'v=S1;d=' + domain + ';n=' + name + ';'; + } } - } - getDomain() { - return this._domain; - } + getDomain() { + return this._domain; + } - getName() { - return this._name; - } + getName() { + return this._name; + } - getCredentials() { - return this._creds; - } + getCredentials() { + return this._creds; + } - getAuthority() { - if (!this._domain){ - return null; + getAuthority() { + if (!this._domain) { + return null; + } + return this; } - return this; - } - getHeader() { - return 'Athenz-Principal-Auth'; - } + getHeader() { + return 'Athenz-Principal-Auth'; + } } module.exports = IdentityMock; diff --git a/clients/nodejs/zts/test/config/KeyStore.js b/clients/nodejs/zts/test/config/KeyStore.js index 1788cfca0c2..eb0a549503d 100644 --- a/clients/nodejs/zts/test/config/KeyStore.js +++ b/clients/nodejs/zts/test/config/KeyStore.js @@ -3,12 +3,12 @@ var fs = require('fs'); class KeyStore { - static getPublicKey(domain, service, keyId) { - if (!domain || !service || !keyId) { - return null; + static getPublicKey(domain, service, keyId) { + if (!domain || !service || !keyId) { + return null; + } + return fs.readFileSync(__dirname + '/../resources/public_k0.pem'); } - return fs.readFileSync(__dirname + '/../resources/public_k0.pem'); - } } module.exports = KeyStore; diff --git a/clients/nodejs/zts/test/config/RdlMock.js b/clients/nodejs/zts/test/config/RdlMock.js index 095af1e06d7..8994680c260 100644 --- a/clients/nodejs/zts/test/config/RdlMock.js +++ b/clients/nodejs/zts/test/config/RdlMock.js @@ -14,17 +14,17 @@ 'use strict'; class rdlMock { - constructor(res, flag) { - this._res = res; - this._flag = flag; - } + constructor(res, flag) { + this._res = res; + this._flag = flag; + } - getRoleToken(params, cb) { - if(!this._flag) { - cb(new Error('rdlMock: Error'), null); + getRoleToken(params, cb) { + if (!this._flag) { + cb(new Error('rdlMock: Error'), null); + } + cb(null, this._res); } - cb(null, this._res); - } } module.exports = rdlMock; diff --git a/clients/nodejs/zts/test/config/RoleTokenMock.js b/clients/nodejs/zts/test/config/RoleTokenMock.js index 341d622f8d0..0524fdc6c5b 100644 --- a/clients/nodejs/zts/test/config/RoleTokenMock.js +++ b/clients/nodejs/zts/test/config/RoleTokenMock.js @@ -14,13 +14,13 @@ 'use strict'; class RoleTokenMock { - constructor(expiryTime) { - this._expiryTime = expiryTime; - } + constructor(expiryTime) { + this._expiryTime = expiryTime; + } - getExpiryTime() { - return Math.floor(Date.now() / 1000) + this._expiryTime; - } + getExpiryTime() { + return Math.floor(Date.now() / 1000) + this._expiryTime; + } } module.exports = RoleTokenMock; diff --git a/clients/nodejs/zts/test/config/SiaProviderMock.js b/clients/nodejs/zts/test/config/SiaProviderMock.js index ace08c3165f..83d419aa890 100644 --- a/clients/nodejs/zts/test/config/SiaProviderMock.js +++ b/clients/nodejs/zts/test/config/SiaProviderMock.js @@ -16,24 +16,24 @@ var identityMock = require('./IdentityMock'); class SiaProviderMock { - constructor(domain, service) { - this._domain = null; - this._service = null; + constructor(domain, service) { + this._domain = null; + this._service = null; - if(domain) { - this._domain = domain.toString().toLowerCase(); + if (domain) { + this._domain = domain.toString().toLowerCase(); + } + if (service) { + this._service = service.toString().toLowerCase(); + } } - if(service) { - this._service = service.toString().toLowerCase(); - } - } - getIdentity(domain, service) { - if (domain !== this._domain || service !== this._service) { - return null; + getIdentity(domain, service) { + if (domain !== this._domain || service !== this._service) { + return null; + } + return new identityMock(this._domain, this._service); } - return new identityMock(this._domain, this._service); - } } module.exports = SiaProviderMock; diff --git a/clients/nodejs/zts/test/config/helpers.js b/clients/nodejs/zts/test/config/helpers.js index 003dca72fd2..5492d70b626 100644 --- a/clients/nodejs/zts/test/config/helpers.js +++ b/clients/nodejs/zts/test/config/helpers.js @@ -1,9 +1,11 @@ 'use strict'; -var privateKey = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBcGZoVnhPT2VaQVhMNkxhYzY3dXFXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOCk53UWs5cU1XSVNHRDZ3NSt4aW5OOUZ2YWtXRndsblJhY1JmaWtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWoKMzlhdXM0aFZuK2lCZjZWVFZWN0tyTVdQMnRkRDkybW4xdTJTM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UgpBQzVVbnpTKzZlTjF2RTRxOTNNMkdXSk1qcVVaZXQ1RHJwQVcrWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5Cnd3SEJDS2lDNW9xNmJWVXlLOUNBay9qejJYSW5oRjB2T0RtQlJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlEKZFJlYVF0dWt3R3JLK0EvdGZBVFYvSWs5Tk14Um95V2FUd2o4MFFJREFRQUJBb0lCQUZTQ0lGb0NkSElGcVpBeQorNi9rZkpjalMwRTlhMlU3VC9SZ2dZREpteDVnTmV6L1JtM1BCR3M1RzFsL1VVWnN1UXg1Mk02bHZxTHI5SUlICmVNM0JGYk1SaHl6QWYycEZ2ajlnbUtTeEJWWkFISlhHY2hwVm05cjZmbTdQMnJCK0kvS0NNN3pKVTdoZ2g5RUEKMFY5VTFNV2I4U0JHUmhPdmh1UW9zZCtMNkpxQmYzOUkrN1BWdFRxNjZuNXVtWGU1WlU1eTJiMmRsSmRTU1hXeQpjc1BSMWUzT1IvTnM5dDVJTVdHcnR2VUowWHlWMjM3eENVM2tmOEg4aDA0NWd6eUdrZHE5djZIVHJhQzZMaTF5CmdwRDhVMFQ2VVQwWHBGcWowbGttTk1NcHNqOWw1QnVSR2ZPNjVhb0ZBOWl5S2xMdndaTnJ2YnNZL0UrNUJjRnMKQWNJczY2a0NnWUVBenAxU1VZZ2FGaWZFQnE3cGluNTY3S3FQemFCcURLYUZTekhkWnFsR1RDVzJ4bDJIY2J4YwpObWJseXUrU21rdzFQZGFlbzF3SXh3U052eU8xWTE1Y0pGbzBteUljUzZRZFJFZmhwWjU0dUdqOE9YQ0RzeW10Ci94TVEwVVlzdFJPaXhDVTlVL1lFdnh3TzlpUkRpU0VsdXd3cDBuN09LdjRRTTVxVkliMFdydjhDZ1lFQXphUDkKbW9ROFFDNS9mRlg0eE5tOFFGOXp5Ukc3SnRndysrNWJYbkFXVFgzcWpIaUxlQ1ZZdHpidlFVY1g0cFp5YlRqaAowYm5yUXpiZmJFVFFZMnk4ZGdqWTVjMEpPNmNTVm11QkE4Lzc4aHl4WUZTTzV0N01CSS93ak42aU80VWhHSGdKCnc3Q1VVM013RDMwWlNZQ3JwNDZyZE5yamdUc3dmN2crc3N1OUpDOENnWUJvMjFhbm9oYjdIM3RRbVB4VkdSTngKZ0s0eWdUTFE4TUc5QTdXRklHdFl3ZHNjbU9MZ1NlUFNpQzRlNjY3UE45WGRhRXBpUlpiK3ljVFdPRjBaN1ExKwpOWGwxTWI2Q2RPdVZkNVdBNUFnSUx0K3lsdk4vdmF0Y1JHVElrSUNuOVNzcHVHeURhOXZFMFl5V1Jwa3Z3dTdQCkdzRXUzc1BxOWIxck13eDBidTVRS1FLQmdBeU1TQ3BJaldDaE5iaEJpcmVBVGNOano2M2lQaGhGc3Q5OGtPaTMKVURVVVRONmJjRzg1WUN0MTE2MlZCL2tVa3hEbEdxcHdmTkdTSkpuM3JQdVVJLy9UMUdCWlhZbmRUUG9tL3kxYgpZSlZLZU94VzNJMXI1T2tXVzJoTklYc2VTWUd6dVd6T2RvNk5CYzY4SkhIZXZ4cXZVdmtEYmtSeGR5a2o5ZmQxCkJTcVRBb0dBVG1nZ05MQk5tNzh0ZWg1cGVyY2RTSXV2VWFMNzRzaXlKbFJlWW5ETitEeUQyUDMwaE9VVUVSYWEKVm9yaFlLdTdTdWVHclBhRy8vYWxZa3hGTGs1Q0RkVmovMWdaS3dTTWc0L3RaRm1yYXBOMFVmdDdGQkx1N1RYTApab1NSeVFsZFo0ZGRpR0xsQ1hlbGh4STdjSnJoQlN3UytYcVVPdnk0V29oYTdsYndYems9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg--"; -var publicKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwZmhWeE9PZVpBWEw2TGFjNjd1cQpXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOTndRazlxTVdJU0dENnc1K3hpbk45RnZha1dGd2xuUmFjUmZpCmtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWozOWF1czRoVm4raUJmNlZUVlY3S3JNV1AydGREOTJtbjF1MlMKM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UkFDNVVuelMrNmVOMXZFNHE5M00yR1dKTWpxVVpldDVEcnBBVworWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5d3dIQkNLaUM1b3E2YlZVeUs5Q0FrL2p6MlhJbmhGMHZPRG1CClJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlFkUmVhUXR1a3dHcksrQS90ZkFUVi9JazlOTXhSb3lXYVR3ajgKMFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--"; +var privateKey = + 'LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBcGZoVnhPT2VaQVhMNkxhYzY3dXFXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOCk53UWs5cU1XSVNHRDZ3NSt4aW5OOUZ2YWtXRndsblJhY1JmaWtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWoKMzlhdXM0aFZuK2lCZjZWVFZWN0tyTVdQMnRkRDkybW4xdTJTM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UgpBQzVVbnpTKzZlTjF2RTRxOTNNMkdXSk1qcVVaZXQ1RHJwQVcrWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5Cnd3SEJDS2lDNW9xNmJWVXlLOUNBay9qejJYSW5oRjB2T0RtQlJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlEKZFJlYVF0dWt3R3JLK0EvdGZBVFYvSWs5Tk14Um95V2FUd2o4MFFJREFRQUJBb0lCQUZTQ0lGb0NkSElGcVpBeQorNi9rZkpjalMwRTlhMlU3VC9SZ2dZREpteDVnTmV6L1JtM1BCR3M1RzFsL1VVWnN1UXg1Mk02bHZxTHI5SUlICmVNM0JGYk1SaHl6QWYycEZ2ajlnbUtTeEJWWkFISlhHY2hwVm05cjZmbTdQMnJCK0kvS0NNN3pKVTdoZ2g5RUEKMFY5VTFNV2I4U0JHUmhPdmh1UW9zZCtMNkpxQmYzOUkrN1BWdFRxNjZuNXVtWGU1WlU1eTJiMmRsSmRTU1hXeQpjc1BSMWUzT1IvTnM5dDVJTVdHcnR2VUowWHlWMjM3eENVM2tmOEg4aDA0NWd6eUdrZHE5djZIVHJhQzZMaTF5CmdwRDhVMFQ2VVQwWHBGcWowbGttTk1NcHNqOWw1QnVSR2ZPNjVhb0ZBOWl5S2xMdndaTnJ2YnNZL0UrNUJjRnMKQWNJczY2a0NnWUVBenAxU1VZZ2FGaWZFQnE3cGluNTY3S3FQemFCcURLYUZTekhkWnFsR1RDVzJ4bDJIY2J4YwpObWJseXUrU21rdzFQZGFlbzF3SXh3U052eU8xWTE1Y0pGbzBteUljUzZRZFJFZmhwWjU0dUdqOE9YQ0RzeW10Ci94TVEwVVlzdFJPaXhDVTlVL1lFdnh3TzlpUkRpU0VsdXd3cDBuN09LdjRRTTVxVkliMFdydjhDZ1lFQXphUDkKbW9ROFFDNS9mRlg0eE5tOFFGOXp5Ukc3SnRndysrNWJYbkFXVFgzcWpIaUxlQ1ZZdHpidlFVY1g0cFp5YlRqaAowYm5yUXpiZmJFVFFZMnk4ZGdqWTVjMEpPNmNTVm11QkE4Lzc4aHl4WUZTTzV0N01CSS93ak42aU80VWhHSGdKCnc3Q1VVM013RDMwWlNZQ3JwNDZyZE5yamdUc3dmN2crc3N1OUpDOENnWUJvMjFhbm9oYjdIM3RRbVB4VkdSTngKZ0s0eWdUTFE4TUc5QTdXRklHdFl3ZHNjbU9MZ1NlUFNpQzRlNjY3UE45WGRhRXBpUlpiK3ljVFdPRjBaN1ExKwpOWGwxTWI2Q2RPdVZkNVdBNUFnSUx0K3lsdk4vdmF0Y1JHVElrSUNuOVNzcHVHeURhOXZFMFl5V1Jwa3Z3dTdQCkdzRXUzc1BxOWIxck13eDBidTVRS1FLQmdBeU1TQ3BJaldDaE5iaEJpcmVBVGNOano2M2lQaGhGc3Q5OGtPaTMKVURVVVRONmJjRzg1WUN0MTE2MlZCL2tVa3hEbEdxcHdmTkdTSkpuM3JQdVVJLy9UMUdCWlhZbmRUUG9tL3kxYgpZSlZLZU94VzNJMXI1T2tXVzJoTklYc2VTWUd6dVd6T2RvNk5CYzY4SkhIZXZ4cXZVdmtEYmtSeGR5a2o5ZmQxCkJTcVRBb0dBVG1nZ05MQk5tNzh0ZWg1cGVyY2RTSXV2VWFMNzRzaXlKbFJlWW5ETitEeUQyUDMwaE9VVUVSYWEKVm9yaFlLdTdTdWVHclBhRy8vYWxZa3hGTGs1Q0RkVmovMWdaS3dTTWc0L3RaRm1yYXBOMFVmdDdGQkx1N1RYTApab1NSeVFsZFo0ZGRpR0xsQ1hlbGh4STdjSnJoQlN3UytYcVVPdnk0V29oYTdsYndYems9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg--'; +var publicKey = + 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwZmhWeE9PZVpBWEw2TGFjNjd1cQpXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOTndRazlxTVdJU0dENnc1K3hpbk45RnZha1dGd2xuUmFjUmZpCmtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWozOWF1czRoVm4raUJmNlZUVlY3S3JNV1AydGREOTJtbjF1MlMKM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UkFDNVVuelMrNmVOMXZFNHE5M00yR1dKTWpxVVpldDVEcnBBVworWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5d3dIQkNLaUM1b3E2YlZVeUs5Q0FrL2p6MlhJbmhGMHZPRG1CClJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlFkUmVhUXR1a3dHcksrQS90ZkFUVi9JazlOTXhSb3lXYVR3ajgKMFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--'; module.exports = { - privateKey: privateKey, - publicKey: publicKey + privateKey: privateKey, + publicKey: publicKey, }; diff --git a/clients/nodejs/zts/test/unit/index.js b/clients/nodejs/zts/test/unit/index.js index 901524bf094..3b4dac240e3 100644 --- a/clients/nodejs/zts/test/unit/index.js +++ b/clients/nodejs/zts/test/unit/index.js @@ -27,11 +27,11 @@ var rdlMock = require('../config/RdlMock'); var sandbox; var params = { - zts: 'zts.athenz.com', - domainName: null, - serviceName: null, - siaProvider: null, - identity: null + zts: 'zts.athenz.com', + domainName: null, + serviceName: null, + siaProvider: null, + identity: null, }; var domainName = 'athenz.user'; @@ -41,395 +41,507 @@ var siaMock = new siaProviderMock(domainName, serviceName); var ideMock = new identityMock(domainName, serviceName); var siaParams = { - zts: 'zts.athenz.com', - domainName: domainName, - serviceName: serviceName, - siaProvider: siaMock, - identity: null + zts: 'zts.athenz.com', + domainName: domainName, + serviceName: serviceName, + siaProvider: siaMock, + identity: null, }; var paramsR = { - domainName: 'athenz.com', - roleName: 'front', - minExpiryTime: 900, - maxExpiryTime: 1400, - ignoreCache: false, - proxyForPrincipal: 'proxy' + domainName: 'athenz.com', + roleName: 'front', + minExpiryTime: 900, + maxExpiryTime: 1400, + ignoreCache: false, + proxyForPrincipal: 'proxy', }; var cacheKey = 'cacheKeyRoleToken'; -describe('zts_client impl', function() { - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); - - afterEach(function() { - sandbox.restore(); - }); - - it('should test zts_client', function() { - var zts = new ztsClient(params); - - expect(zts).to.not.be.null; - - expect(zts._ztsUrl).to.equal(params.zts); - expect(zts._domain).to.be.null; - expect(zts._service).to.be.null; - expect(zts._siaProvider).to.be.null; - expect(zts.principal).to.be.null; - - expect(zts.cacheDisabled).to.not.be.null; - expect(zts.tokenMinExpiryTime).to.not.be.null; - expect(zts.prefetchInterval).to.not.be.null; - expect(zts.prefetchAutoEnable).to.not.be.null; - - expect(zts._enablePrefetch).to.be.false; - }); - - it('should test _initConfigValues', function() { - try { - ztsClient._initConfigValues(); - } catch (e) { - expect(1).to.be.false; - } - }); - - it('should test _initClient: domainName & serviceName & siaProvider', function() { - var zts = new ztsClient(siaParams); - - expect(zts._ztsUrl).to.equal(siaParams.zts); - expect(zts._domain).to.equal(siaParams.domainName); - expect(zts._service).to.equal(siaParams.serviceName); - expect(zts._siaProvider).to.equal(siaParams.siaProvider); - expect(zts.principal).to.be.null; - }); - - it('should test _initClient: domainName or serviceName or siaProvidr be null: result error', function() { - var pars = Object.assign({}, params); +describe('zts_client impl', function () { + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); - pars.siaProvider = siaMock; + afterEach(function () { + sandbox.restore(); + }); - try { - new ztsClient(pars); - } catch (e) { - expect(e.message).to.contain('domainName & serviceName & siaProvider must be specified.'); - return; - } - expect(1).to.be.false; - }); + it('should test zts_client', function () { + var zts = new ztsClient(params); - it('should test _initClient: identity', function() { - var pars = Object.assign({}, params); - pars.ztsUrl = null; + expect(zts).to.not.be.null; - pars.identity = ideMock; - var zts = new ztsClient(pars); + expect(zts._ztsUrl).to.equal(params.zts); + expect(zts._domain).to.be.null; + expect(zts._service).to.be.null; + expect(zts._siaProvider).to.be.null; + expect(zts.principal).to.be.null; - expect(zts._ztsUrl).to.not.be.null; - expect(zts._domain).to.equal(pars.identity.getDomain()); - expect(zts._service).to.equal(pars.identity.getName()); - expect(zts._siaProvider).to.be.null; - expect(zts.principal).to.equal(pars.identity); - expect(zts._ztsClient).to.not.be.null; - }); + expect(zts.cacheDisabled).to.not.be.null; + expect(zts.tokenMinExpiryTime).to.not.be.null; + expect(zts.prefetchInterval).to.not.be.null; + expect(zts.prefetchAutoEnable).to.not.be.null; - it('should test _initClient: identity.getAuthority() null: result error', function() { - var pars = Object.assign({}, params); + expect(zts._enablePrefetch).to.be.false; + }); - var ideMockInv = new identityMock(null, null); - pars.identity = ideMockInv; + it('should test _initConfigValues', function () { + try { + ztsClient._initConfigValues(); + } catch (e) { + expect(1).to.be.false; + } + }); - try { - new ztsClient(pars); - } catch (e) { - expect(e.message).to.contain('Principal Authority cannot be null'); - return; - } - expect(1).to.be.false; - }); + it('should test _initClient: domainName & serviceName & siaProvider', function () { + var zts = new ztsClient(siaParams); - it('should test _addPrincipalCredentials: resetServiceDetails true', function() { - var zts = new ztsClient(params); + expect(zts._ztsUrl).to.equal(siaParams.zts); + expect(zts._domain).to.equal(siaParams.domainName); + expect(zts._service).to.equal(siaParams.serviceName); + expect(zts._siaProvider).to.equal(siaParams.siaProvider); + expect(zts.principal).to.be.null; + }); - zts._addPrincipalCredentials(ideMock, true); + it('should test _initClient: domainName or serviceName or siaProvidr be null: result error', function () { + var pars = Object.assign({}, params); - expect(zts._ztsClient).to.not.be.null; - expect(zts._siaProvider).to.be.null; - expect(zts.principal).to.equal(ideMock); - }); + pars.siaProvider = siaMock; - it('should test _addPrincipalCredentials: resetServiceDetails false', function() { - var zts = new ztsClient(siaParams); + try { + new ztsClient(pars); + } catch (e) { + expect(e.message).to.contain( + 'domainName & serviceName & siaProvider must be specified.' + ); + return; + } + expect(1).to.be.false; + }); - zts._addPrincipalCredentials(ideMock, false); + it('should test _initClient: identity', function () { + var pars = Object.assign({}, params); + pars.ztsUrl = null; - expect(zts._ztsClient).to.not.be.null; - expect(zts._siaProvider).to.equal(siaMock); - expect(zts.principal).to.equal(ideMock); - }); + pars.identity = ideMock; + var zts = new ztsClient(pars); - it('should test _addPrincipalCredentials: identity null', function() { - var zts = new ztsClient(params); + expect(zts._ztsUrl).to.not.be.null; + expect(zts._domain).to.equal(pars.identity.getDomain()); + expect(zts._service).to.equal(pars.identity.getName()); + expect(zts._siaProvider).to.be.null; + expect(zts.principal).to.equal(pars.identity); + expect(zts._ztsClient).to.not.be.null; + }); - zts._addPrincipalCredentials(null, true); + it('should test _initClient: identity.getAuthority() null: result error', function () { + var pars = Object.assign({}, params); - expect(zts._ztsClient).to.be.null; - expect(zts.principal).to.be.null; - }); + var ideMockInv = new identityMock(null, null); + pars.identity = ideMockInv; - it('should test _addPrincipalCredentials: identity.getAuthority() null', function() { - var zts = new ztsClient(params); + try { + new ztsClient(pars); + } catch (e) { + expect(e.message).to.contain('Principal Authority cannot be null'); + return; + } + expect(1).to.be.false; + }); - var ideMockInv = new identityMock(null, null); - zts._addPrincipalCredentials(ideMockInv, true); + it('should test _addPrincipalCredentials: resetServiceDetails true', function () { + var zts = new ztsClient(params); - expect(zts._ztsClient).to.be.null; - expect(zts.principal).to.equal(ideMockInv); - }); + zts._addPrincipalCredentials(ideMock, true); - it('should test _sameCredentialsAsBefore', function() { - var pars = Object.assign({}, params); - pars.identity = ideMock; - var zts = new ztsClient(pars); + expect(zts._ztsClient).to.not.be.null; + expect(zts._siaProvider).to.be.null; + expect(zts.principal).to.equal(ideMock); + }); - expect(zts._sameCredentialsAsBefore(ideMock)).to.be.true; - }); + it('should test _addPrincipalCredentials: resetServiceDetails false', function () { + var zts = new ztsClient(siaParams); - it('should test _sameCredentialsAsBefore: principal null: result false', function() { - var zts = new ztsClient(params); + zts._addPrincipalCredentials(ideMock, false); - expect(zts._sameCredentialsAsBefore(ideMock)).to.be.false; - }); + expect(zts._ztsClient).to.not.be.null; + expect(zts._siaProvider).to.equal(siaMock); + expect(zts.principal).to.equal(ideMock); + }); - it('should test _sameCredentialsAsBefore: principal.getCredentials() null: result false', function() { - var pars = Object.assign({}, params); + it('should test _addPrincipalCredentials: identity null', function () { + var zts = new ztsClient(params); - var ideMockInv = new identityMock(domainName, null); + zts._addPrincipalCredentials(null, true); - pars.identity = ideMockInv; - var zts = new ztsClient(pars); + expect(zts._ztsClient).to.be.null; + expect(zts.principal).to.be.null; + }); - expect(zts._sameCredentialsAsBefore(ideMock)).to.be.false; - }); + it('should test _addPrincipalCredentials: identity.getAuthority() null', function () { + var zts = new ztsClient(params); - it('should test _sameCredentialsAsBefore: no match creds: result false', function() { - var pars = Object.assign({}, params); - pars.identity = ideMock; - var zts = new ztsClient(pars); + var ideMockInv = new identityMock(null, null); + zts._addPrincipalCredentials(ideMockInv, true); - var ideMockAn = new identityMock('athenz.user', 'test2'); + expect(zts._ztsClient).to.be.null; + expect(zts.principal).to.equal(ideMockInv); + }); - expect(zts._sameCredentialsAsBefore(ideMockAn)).to.be.false; - }); + it('should test _sameCredentialsAsBefore', function () { + var pars = Object.assign({}, params); + pars.identity = ideMock; + var zts = new ztsClient(pars); - it('should test _updateServicePrincipal', function() { - var zts = new ztsClient(siaParams); + expect(zts._sameCredentialsAsBefore(ideMock)).to.be.true; + }); - expect(zts._updateServicePrincipal()).to.be.true; - }); + it('should test _sameCredentialsAsBefore: principal null: result false', function () { + var zts = new ztsClient(params); - it('should test _updateServicePrincipal: siaProvider null: result false', function() { - var zts = new ztsClient(params); + expect(zts._sameCredentialsAsBefore(ideMock)).to.be.false; + }); - expect(zts._updateServicePrincipal()).to.be.false; - }); + it('should test _sameCredentialsAsBefore: principal.getCredentials() null: result false', function () { + var pars = Object.assign({}, params); - it('should test _updateServicePrincipal: siaProvider.getIdentity null: result error', function() { - var pars = Object.assign({}, siaParams); + var ideMockInv = new identityMock(domainName, null); - var siaMockInv = new siaProviderMock(domainName, null); + pars.identity = ideMockInv; + var zts = new ztsClient(pars); - pars.siaProvider = siaMockInv; - var zts = new ztsClient(pars); + expect(zts._sameCredentialsAsBefore(ideMock)).to.be.false; + }); - try { - zts._updateServicePrincipal(); - } catch (e) { - expect(e.message).to.contain('UpdateServicePrincipal: Unable to get PrincipalToken'); - return; - } - expect(1).to.be.false; - }); + it('should test _sameCredentialsAsBefore: no match creds: result false', function () { + var pars = Object.assign({}, params); + pars.identity = ideMock; + var zts = new ztsClient(pars); - it('should test _updateServicePrincipal: identity null: result false', function() { - var zts = new ztsClient(siaParams); + var ideMockAn = new identityMock('athenz.user', 'test2'); - zts._addPrincipalCredentials(ideMock, false); + expect(zts._sameCredentialsAsBefore(ideMockAn)).to.be.false; + }); - expect(zts._updateServicePrincipal()).to.be.false; - }); + it('should test _updateServicePrincipal', function () { + var zts = new ztsClient(siaParams); - it('should test getRoleToken: get cache', function() { - var zts = new ztsClient(siaParams); + expect(zts._updateServicePrincipal()).to.be.true; + }); - var roleToken = new roleTokenMock(20000); - var ck = 'p=' + domainName + '.' + serviceName + ';d=' + paramsR.domainName + ';r=' + paramsR.roleName + ';u=' + paramsR.proxyForPrincipal; - cache.del(ck); - cache.put(ck, roleToken); + it('should test _updateServicePrincipal: siaProvider null: result false', function () { + var zts = new ztsClient(params); - var rT = null; - zts.getRoleToken(paramsR, function(err, res) { - if (err) { - throw err; - } - rT = res; + expect(zts._updateServicePrincipal()).to.be.false; }); - expect(rT).to.equal(roleToken); - }); - it('should test getRoleToken: domainName Null', function() { - var zts = new ztsClient(siaParams); + it('should test _updateServicePrincipal: siaProvider.getIdentity null: result error', function () { + var pars = Object.assign({}, siaParams); - var parsR = Object.assign({}, paramsR); - parsR.domainName = null; + var siaMockInv = new siaProviderMock(domainName, null); - var rT = null; - try { - zts.getRoleToken(parsR, function(err, res) { - if (err) { - throw err; - } - rT = res; - }); - } catch (e) { - expect(e.message).to.contain('GetRoleToken: domainName must not be null.'); - return; - } - expect(1).to.be.false; - }); - - it('should test getRoleToken: no cache & get RoleToken', function() { - var pars = Object.assign({}, params); - pars.identity = ideMock; - var zts = new ztsClient(pars); - - var roleToken = new roleTokenMock(20000); - zts._ztsClient = new rdlMock(roleToken, true); - - var ck = 'p=' + domainName + '.' + serviceName + ';d=' + paramsR.domainName + ';r=' + paramsR.roleName + ';u=' + paramsR.proxyForPrincipal; - cache.del(ck); - - var rT = null; - zts.getRoleToken(paramsR, function(err, res) { - if (err) { - throw err; - } - rT = res; - }); - expect(rT).to.equal(roleToken); - expect(cache.get(ck)).to.equal(roleToken); - }); - - it('should test getRoleToken: no cache & cannot get RoleToken: result error', function() { - var pars = Object.assign({}, params); - pars.identity = ideMock; - var zts = new ztsClient(pars); - - var roleToken = new roleTokenMock(20000); - zts._ztsClient = new rdlMock(roleToken, false); - - var ck = 'p=' + domainName + '.' + serviceName + ';d=' + paramsR.domainName + ';r=' + paramsR.roleName + ';u=' + paramsR.proxyForPrincipal; - cache.del(ck); - - var rT = null; - try { - zts.getRoleToken(paramsR, function(err, res) { - if (err) { - throw err; + pars.siaProvider = siaMockInv; + var zts = new ztsClient(pars); + + try { + zts._updateServicePrincipal(); + } catch (e) { + expect(e.message).to.contain( + 'UpdateServicePrincipal: Unable to get PrincipalToken' + ); + return; } - rT = res; - }); - } catch (e) { - expect(e.message).to.contain('rdlMock: Error'); - return; - } - expect(1).to.be.false; - }); + expect(1).to.be.false; + }); - it('should test getRoleToken: cannot get cacheKey', function() { - var pars = Object.assign({}, params); - var zts = new ztsClient(pars); + it('should test _updateServicePrincipal: identity null: result false', function () { + var zts = new ztsClient(siaParams); - var roleToken = new roleTokenMock(20000); - zts._ztsClient = new rdlMock(roleToken, true); + zts._addPrincipalCredentials(ideMock, false); - var rT = null; - zts.getRoleToken(paramsR, function(err, res) { - if (err) { - throw err; - } - rT = res; + expect(zts._updateServicePrincipal()).to.be.false; }); - expect(rT).to.equal(roleToken); - }); - it('should test _getRoleTokenCacheKeySetTenant', function() { - var pars = Object.assign({}, params); - pars.identity = ideMock; - var zts = new ztsClient(pars); + it('should test getRoleToken: get cache', function () { + var zts = new ztsClient(siaParams); + + var roleToken = new roleTokenMock(20000); + var ck = + 'p=' + + domainName + + '.' + + serviceName + + ';d=' + + paramsR.domainName + + ';r=' + + paramsR.roleName + + ';u=' + + paramsR.proxyForPrincipal; + cache.del(ck); + cache.put(ck, roleToken); + + var rT = null; + zts.getRoleToken(paramsR, function (err, res) { + if (err) { + throw err; + } + rT = res; + }); + expect(rT).to.equal(roleToken); + }); - expect(zts._getRoleTokenCacheKeySetTenant(paramsR.domainName, paramsR.roleName, paramsR.proxyForPrincipal)).to.equal('p=' + domainName + '.' + serviceName + ';d=' + paramsR.domainName + ';r=' + paramsR.roleName + ';u=' + paramsR.proxyForPrincipal); - }); + it('should test getRoleToken: domainName Null', function () { + var zts = new ztsClient(siaParams); + + var parsR = Object.assign({}, paramsR); + parsR.domainName = null; + + var rT = null; + try { + zts.getRoleToken(parsR, function (err, res) { + if (err) { + throw err; + } + rT = res; + }); + } catch (e) { + expect(e.message).to.contain( + 'GetRoleToken: domainName must not be null.' + ); + return; + } + expect(1).to.be.false; + }); - it('should test _getRoleTokenCacheKey: tenantDomain null: result null', function() { - var zts = new ztsClient(params); + it('should test getRoleToken: no cache & get RoleToken', function () { + var pars = Object.assign({}, params); + pars.identity = ideMock; + var zts = new ztsClient(pars); + + var roleToken = new roleTokenMock(20000); + zts._ztsClient = new rdlMock(roleToken, true); + + var ck = + 'p=' + + domainName + + '.' + + serviceName + + ';d=' + + paramsR.domainName + + ';r=' + + paramsR.roleName + + ';u=' + + paramsR.proxyForPrincipal; + cache.del(ck); + + var rT = null; + zts.getRoleToken(paramsR, function (err, res) { + if (err) { + throw err; + } + rT = res; + }); + expect(rT).to.equal(roleToken); + expect(cache.get(ck)).to.equal(roleToken); + }); - expect(zts._getRoleTokenCacheKey(null, serviceName, paramsR.domainName, paramsR.roleName, paramsR.proxyForPrincipal)).to.be.null; - }); + it('should test getRoleToken: no cache & cannot get RoleToken: result error', function () { + var pars = Object.assign({}, params); + pars.identity = ideMock; + var zts = new ztsClient(pars); + + var roleToken = new roleTokenMock(20000); + zts._ztsClient = new rdlMock(roleToken, false); + + var ck = + 'p=' + + domainName + + '.' + + serviceName + + ';d=' + + paramsR.domainName + + ';r=' + + paramsR.roleName + + ';u=' + + paramsR.proxyForPrincipal; + cache.del(ck); + + var rT = null; + try { + zts.getRoleToken(paramsR, function (err, res) { + if (err) { + throw err; + } + rT = res; + }); + } catch (e) { + expect(e.message).to.contain('rdlMock: Error'); + return; + } + expect(1).to.be.false; + }); - it('should test _getRoleTokenCacheKey: tenantService null', function() { - var zts = new ztsClient(params); + it('should test getRoleToken: cannot get cacheKey', function () { + var pars = Object.assign({}, params); + var zts = new ztsClient(pars); + + var roleToken = new roleTokenMock(20000); + zts._ztsClient = new rdlMock(roleToken, true); + + var rT = null; + zts.getRoleToken(paramsR, function (err, res) { + if (err) { + throw err; + } + rT = res; + }); + expect(rT).to.equal(roleToken); + }); - expect(zts._getRoleTokenCacheKey(domainName, null, paramsR.domainName, paramsR.roleName, paramsR.proxyForPrincipal)).to.equal('p=' + domainName + ';d=' + paramsR.domainName + ';r=' + paramsR.roleName + ';u=' + paramsR.proxyForPrincipal); - }); + it('should test _getRoleTokenCacheKeySetTenant', function () { + var pars = Object.assign({}, params); + pars.identity = ideMock; + var zts = new ztsClient(pars); + + expect( + zts._getRoleTokenCacheKeySetTenant( + paramsR.domainName, + paramsR.roleName, + paramsR.proxyForPrincipal + ) + ).to.equal( + 'p=' + + domainName + + '.' + + serviceName + + ';d=' + + paramsR.domainName + + ';r=' + + paramsR.roleName + + ';u=' + + paramsR.proxyForPrincipal + ); + }); - it('should test _getRoleTokenCacheKey: roleName null', function() { - var zts = new ztsClient(params); + it('should test _getRoleTokenCacheKey: tenantDomain null: result null', function () { + var zts = new ztsClient(params); + + expect( + zts._getRoleTokenCacheKey( + null, + serviceName, + paramsR.domainName, + paramsR.roleName, + paramsR.proxyForPrincipal + ) + ).to.be.null; + }); - expect(zts._getRoleTokenCacheKey(domainName, serviceName, paramsR.domainName, null, paramsR.proxyForPrincipal)).to.equal('p=' + domainName + '.' + serviceName + ';d=' + paramsR.domainName + ';u=' + paramsR.proxyForPrincipal); - }); + it('should test _getRoleTokenCacheKey: tenantService null', function () { + var zts = new ztsClient(params); + + expect( + zts._getRoleTokenCacheKey( + domainName, + null, + paramsR.domainName, + paramsR.roleName, + paramsR.proxyForPrincipal + ) + ).to.equal( + 'p=' + + domainName + + ';d=' + + paramsR.domainName + + ';r=' + + paramsR.roleName + + ';u=' + + paramsR.proxyForPrincipal + ); + }); - it('should test _getRoleTokenCacheKey: proxyForPrincipal null', function() { - var zts = new ztsClient(params); + it('should test _getRoleTokenCacheKey: roleName null', function () { + var zts = new ztsClient(params); + + expect( + zts._getRoleTokenCacheKey( + domainName, + serviceName, + paramsR.domainName, + null, + paramsR.proxyForPrincipal + ) + ).to.equal( + 'p=' + + domainName + + '.' + + serviceName + + ';d=' + + paramsR.domainName + + ';u=' + + paramsR.proxyForPrincipal + ); + }); - expect(zts._getRoleTokenCacheKey(domainName, serviceName, paramsR.domainName, paramsR.roleName, null)).to.equal('p=' + domainName + '.' + serviceName + ';d=' + paramsR.domainName + ';r=' + paramsR.roleName); - }); + it('should test _getRoleTokenCacheKey: proxyForPrincipal null', function () { + var zts = new ztsClient(params); + + expect( + zts._getRoleTokenCacheKey( + domainName, + serviceName, + paramsR.domainName, + paramsR.roleName, + null + ) + ).to.equal( + 'p=' + + domainName + + '.' + + serviceName + + ';d=' + + paramsR.domainName + + ';r=' + + paramsR.roleName + ); + }); - it('should test _isExpiredToken', function() { - var zts = new ztsClient(params); + it('should test _isExpiredToken', function () { + var zts = new ztsClient(params); - expect(zts._isExpiredToken(60, 100, 150, 900)).to.be.true; - expect(zts._isExpiredToken(180, 100, 150, 900)).to.be.true; - expect(zts._isExpiredToken(60, null, null, 900)).to.be.true; - expect(zts._isExpiredToken(120, 100, 150, 900)).to.be.false; - expect(zts._isExpiredToken(910, null, null, 900)).to.be.false; - }); + expect(zts._isExpiredToken(60, 100, 150, 900)).to.be.true; + expect(zts._isExpiredToken(180, 100, 150, 900)).to.be.true; + expect(zts._isExpiredToken(60, null, null, 900)).to.be.true; + expect(zts._isExpiredToken(120, 100, 150, 900)).to.be.false; + expect(zts._isExpiredToken(910, null, null, 900)).to.be.false; + }); - it('should test _lookupRoleTokenInCache', function() { - var zts = new ztsClient(params); + it('should test _lookupRoleTokenInCache', function () { + var zts = new ztsClient(params); - var roleToken = new roleTokenMock(20000); - cache.del(cacheKey); - cache.put(cacheKey, roleToken); + var roleToken = new roleTokenMock(20000); + cache.del(cacheKey); + cache.put(cacheKey, roleToken); - expect(zts._lookupRoleTokenInCache(cacheKey)).to.equal(roleToken); - }); + expect(zts._lookupRoleTokenInCache(cacheKey)).to.equal(roleToken); + }); - it('should test _lookupRoleTokenInCache: no cache: result null', function() { - var zts = new ztsClient(params); - cache.del(cacheKey); + it('should test _lookupRoleTokenInCache: no cache: result null', function () { + var zts = new ztsClient(params); + cache.del(cacheKey); - expect(zts._lookupRoleTokenInCache(cacheKey)).to.be.null; - }); + expect(zts._lookupRoleTokenInCache(cacheKey)).to.be.null; + }); - it('should test _lookupRoleTokenInCache: exceed expiryTime: result null', function() { - var zts = new ztsClient(params); + it('should test _lookupRoleTokenInCache: exceed expiryTime: result null', function () { + var zts = new ztsClient(params); - var roleToken = new roleTokenMock(100); - cache.del(cacheKey); - cache.put(cacheKey, roleToken); + var roleToken = new roleTokenMock(100); + cache.del(cacheKey); + cache.put(cacheKey, roleToken); - expect(zts._lookupRoleTokenInCache(cacheKey)).to.equal(roleToken); - }); + expect(zts._lookupRoleTokenInCache(cacheKey)).to.equal(roleToken); + }); }); diff --git a/libs/nodejs/auth_core/.prettierrc b/libs/nodejs/auth_core/.prettierrc new file mode 100644 index 00000000000..6bb22e39328 --- /dev/null +++ b/libs/nodejs/auth_core/.prettierrc @@ -0,0 +1,10 @@ +arrowParens: always +bracketSpacing: true +endOfLine: lf +jsxBracketSameLine: false +jsxSingleQuote: true +semi: true +singleQuote: true +tabWidth: 4 +trailingComma: es5 +useTabs: false diff --git a/libs/nodejs/auth_core/Gruntfile.js b/libs/nodejs/auth_core/Gruntfile.js deleted file mode 100644 index 9f9a6316f54..00000000000 --- a/libs/nodejs/auth_core/Gruntfile.js +++ /dev/null @@ -1,82 +0,0 @@ -/*jshint camelcase: false*/ -'use strict'; - -//var config = require('./config/config.js')(); - -module.exports = function(grunt) { - require('load-grunt-tasks')(grunt); - var path = require('path'), - pretty = require('prettysize'); - - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - copy: {}, - clean: ['build/', 'artifacts/'], - browserify: {}, - eslint: { - options: { - cache: true, - cacheFile: '.eslintcodecache', - configFile: 'eslint.json' - }, - target: ['src/**/*.js', './*.js', 'test/**/*.js'] - }, - filesize: {}, - jshint: { - options: { - globalstrict: true, - expr: true, - esversion: 6, - globals: { - "expect": true, - "assert": false, - "it": false, - "require": false, - "describe": false, - "beforeEach": false, - "afterEach": false, - "before": false, - "after": false, - "$": false, - "$$": false, - "browser": false, - "Buffer": false, - "module": false, - "global": false, - "exports": false, - "process": false, - "console": false, - "__dirname": false, - "Intl": false, - } - }, - files: ['Gruntfile.js', 'src/**/*.js', './*.js', 'test/**/*.js'], - } - }); - - grunt.registerTask('test', 'Run tests', function(){ - grunt.log.ok('Running unit tests with node@' + process.version); - var done = this.async(); - grunt.util.spawn({ - cmd: path.join(__dirname, 'node_modules/nyc/bin/nyc.js'), - args: ['node_modules/.bin/jenkins-mocha', '--require', 'test/config/mock.js', 'test/unit/**/*.js'], - opts: { stdio: 'inherit' } - }, done); - }); - - grunt.registerTask('lint', function() { - grunt.task.run(['jshint', 'eslint']); - }); - - grunt.registerTask('build', function() { - grunt.task.run(['clean']); - }); - - grunt.registerTask('build-dev', function() { - grunt.task.run(['clean']); - }); - - grunt.registerTask('default', ['lint', 'build', 'test']); - - grunt.registerTask('local', ['build-dev']); -}; diff --git a/libs/nodejs/auth_core/Makefile b/libs/nodejs/auth_core/Makefile index 938b770416f..d527e6dbf8e 100644 --- a/libs/nodejs/auth_core/Makefile +++ b/libs/nodejs/auth_core/Makefile @@ -10,10 +10,8 @@ NPM := $(shell command -v npm 2> /dev/null) ifdef NPM -all: - npm install - $(PWD)/node_modules/grunt-cli/bin/grunt - +.PHONY: build lint test +all: build lint test else all: @@ -21,5 +19,17 @@ all: endif +build: + @echo "Make: Build" + npm install + +lint: + @echo "Make: lint" + npm run ci-lint + +test: + @echo "Make: unit tests" + npm run test + clean: - rm -rf build node_modules artifacts + rm -rf node_modules artifacts diff --git a/libs/nodejs/auth_core/eslint.json b/libs/nodejs/auth_core/eslint.json deleted file mode 100644 index b4c9af40abc..00000000000 --- a/libs/nodejs/auth_core/eslint.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "env": { - "es6": true - }, - "rules": { - "eqeqeq": "warn", - "curly": "warn", - "no-undef": "off", - "no-undefined": "off", - "no-unused-vars": "off", - "max-params": ["error", {"max":5}], - "complexity": ["error", {"max":8}] - } -} diff --git a/libs/nodejs/auth_core/index.js b/libs/nodejs/auth_core/index.js index 77cf1341643..fbd0570bde0 100644 --- a/libs/nodejs/auth_core/index.js +++ b/libs/nodejs/auth_core/index.js @@ -14,14 +14,14 @@ 'use strict'; module.exports = { - Crypto: require('./src/util/Crypto'), - KeyStore: require('./src/impl/KeyStore'), - PrincipalAuthority: require('./src/impl/PrincipalAuthority'), - PrincipalToken: require('./src/token/PrincipalToken'), - RoleAuthority: require('./src/impl/RoleAuthority'), - RoleToken: require('./src/token/RoleToken'), - SimplePrincipal: require('./src/impl/SimplePrincipal'), - SimpleServiceIdentityProvider: require('./src/impl/SimpleServiceIdentityProvider'), - Validate: require('./src/util/Validate'), - YBase64: require('./src/util/YBase64') + Crypto: require('./src/util/Crypto'), + KeyStore: require('./src/impl/KeyStore'), + PrincipalAuthority: require('./src/impl/PrincipalAuthority'), + PrincipalToken: require('./src/token/PrincipalToken'), + RoleAuthority: require('./src/impl/RoleAuthority'), + RoleToken: require('./src/token/RoleToken'), + SimplePrincipal: require('./src/impl/SimplePrincipal'), + SimpleServiceIdentityProvider: require('./src/impl/SimpleServiceIdentityProvider'), + Validate: require('./src/util/Validate'), + YBase64: require('./src/util/YBase64'), }; diff --git a/libs/nodejs/auth_core/package.json b/libs/nodejs/auth_core/package.json index c995ad2d127..64f80a849d4 100644 --- a/libs/nodejs/auth_core/package.json +++ b/libs/nodejs/auth_core/package.json @@ -11,10 +11,10 @@ }, "scripts": { "build": "make", - "lint": "grunt lint", - "test": "./node_modules/nyc/bin/nyc.js node_modules/.bin/jenkins-mocha --require test/config/mock.js test/unit/*.js", - "testDev": "mocha --require test/config/mock.js test/unit/*.js", - "functional": "grunt functional-sd", + "fix-lint": "prettier --write src/**/*.js ./*.js test/**/*.js", + "ci-lint": "prettier --list-different src/**/*.js ./*.js test/**/*.js", + "test": "./node_modules/nyc/bin/nyc.js node_modules/.bin/jenkins-mocha --require test/config/mock.js test/unit/**/*.js", + "testDev": "mocha --require test/config/mock.js test/unit/**/*.js", "noop": "echo Ignoring this section." }, "keywords": [ @@ -32,29 +32,16 @@ "winston": "^2.3.1" }, "devDependencies": { + "prettier": "^2.3.0", "chai": "~3.5.0", "chai-as-promised": "~6.0.0", "chart.js": "^2.2.1", "csslint": "^1.0.5", "jshint": "^2.9.4", - "grunt": "~0.4.5", - "grunt-contrib-jshint": "^1.1.0", - "grunt-browserify": "~5.0.0", - "grunt-cli": "~1.2.0", - "grunt-contrib-clean": "~0.7.0", - "grunt-contrib-copy": "~1.0.0", - "grunt-contrib-watch": "^1.0.0", - "grunt-eslint": "^19.0.0", - "grunt-exec": "~1.0.1", - "grunt-filesize": "0.0.7", - "grunt-postcss": "^0.8.0", - "grunt-protractor-webdriver": "~0.2.5", - "grunt-webdriver": "~1.0.0", "nyc": "^11.0.3", "jenkins-mocha": "^5.0.0", "jquery": "^3.1.1", "jquery-ui": "^1.10.5", - "load-grunt-tasks": "^3.5.2", "minifyify": "~7.3.5", "mkdirp": "~0.5.1", "mocha": "~3.2.0", diff --git a/libs/nodejs/auth_core/src/impl/KeyStore.js b/libs/nodejs/auth_core/src/impl/KeyStore.js index 94239694294..7abf3783e91 100644 --- a/libs/nodejs/auth_core/src/impl/KeyStore.js +++ b/libs/nodejs/auth_core/src/impl/KeyStore.js @@ -16,12 +16,20 @@ var fs = require('fs'); class KeyStore { - static getPublicKey(domain, service, keyId) { - if (!domain || !service || !keyId) { - return null; + static getPublicKey(domain, service, keyId) { + if (!domain || !service || !keyId) { + return null; + } + return fs.readFileSync( + 'keys/' + + domain.toString().toLowerCase() + + '.' + + service.toString().toLowerCase() + + '.v' + + keyId + + '.*' + ); } - return fs.readFileSync('keys/' + domain.toString().toLowerCase() + '.' + service.toString().toLowerCase() + '.v' + keyId + '.*'); - } } module.exports = KeyStore; diff --git a/libs/nodejs/auth_core/src/impl/PrincipalAuthority.js b/libs/nodejs/auth_core/src/impl/PrincipalAuthority.js index e1f5ea08fca..bea313c86ac 100644 --- a/libs/nodejs/auth_core/src/impl/PrincipalAuthority.js +++ b/libs/nodejs/auth_core/src/impl/PrincipalAuthority.js @@ -29,240 +29,307 @@ var ATHENZ_PROP_USER_DOMAIN; var ATHENZ_PROP_PRINCIPAL_HEADER; class PrincipalAuthority { - constructor() { - winston.level = config.logLevel; - - ATHENZ_PROP_TOKEN_OFFSET = Number(config.principalTokenAllowedOffset); - ATHENZ_PROP_IP_CHECK_MODE = config.principalIpCheckMode; - ATHENZ_PROP_USER_DOMAIN = config.principalUserDomain; - ATHENZ_PROP_PRINCIPAL_HEADER = config.principalHeader; - - this._keyStore = null; - this._allowedOffset = (ATHENZ_PROP_TOKEN_OFFSET) ? Number(ATHENZ_PROP_TOKEN_OFFSET) : 300; - this._ipCheckMode = ATHENZ_PROP_IP_CHECK_MODE || 'OPS_WRITE'; - this._userDomain = ATHENZ_PROP_USER_DOMAIN || 'user'; - this._headerName = ATHENZ_PROP_PRINCIPAL_HEADER || 'Athenz-Principal-Auth'; - - // case of invalid value, we'll default back to 5 minutes - if (this._allowedOffset < 0) { - this._allowedOffset = 300; - } - } - - static setConfig(c) { - config = Object.assign({}, config, c.auth_core); - PrincipalToken.setConfig(c); - SimplePrincipal.setConfig(c); - } - - initialize() { - } - - getDomain() { - return null; - } - - getHeader() { - return this._headerName; - } - - authenticate(signedToken, remoteAddr, httpMethod) { - winston.debug('Authenticating PrincipalToken: ' + signedToken); - - var serviceToken = null; - try { - serviceToken = new PrincipalToken(signedToken); - } catch (e) { - winston.error('PrincipalAuthority:authenticate: Invalid token: exc=' + e.message + - ' : credential=' + PrincipalToken.getUnsignedToken(signedToken)); - return null; + constructor() { + winston.level = config.logLevel; + + ATHENZ_PROP_TOKEN_OFFSET = Number(config.principalTokenAllowedOffset); + ATHENZ_PROP_IP_CHECK_MODE = config.principalIpCheckMode; + ATHENZ_PROP_USER_DOMAIN = config.principalUserDomain; + ATHENZ_PROP_PRINCIPAL_HEADER = config.principalHeader; + + this._keyStore = null; + this._allowedOffset = ATHENZ_PROP_TOKEN_OFFSET + ? Number(ATHENZ_PROP_TOKEN_OFFSET) + : 300; + this._ipCheckMode = ATHENZ_PROP_IP_CHECK_MODE || 'OPS_WRITE'; + this._userDomain = ATHENZ_PROP_USER_DOMAIN || 'user'; + this._headerName = + ATHENZ_PROP_PRINCIPAL_HEADER || 'Athenz-Principal-Auth'; + + // case of invalid value, we'll default back to 5 minutes + if (this._allowedOffset < 0) { + this._allowedOffset = 300; + } } - /* before authenticating verify that if this is a valid - * authorized service token or not and if required - * components are provided (the method already logs - * all error messages) */ - if (!serviceToken.isValidAuthorizedServiceToken()) { - winston.error('PrincipalAuthority:authenticate: Invalid authorized service token: credential=' + - PrincipalToken.getUnsignedToken(signedToken)); - return null; + static setConfig(c) { + config = Object.assign({}, config, c.auth_core); + PrincipalToken.setConfig(c); + SimplePrincipal.setConfig(c); } - var tokenDomain = serviceToken.getDomain().toString().toLowerCase(); - var tokenName = serviceToken.getName().toString().toLowerCase(); - var keyService = serviceToken.getKeyService(); - var userToken = (tokenDomain === this._userDomain); - - /* get the public key for this token to validate signature */ - var publicKey = this._getPublicKey(tokenDomain, tokenName, keyService, serviceToken.getKeyId(), userToken); - - /* the validate method logs all error messages */ - var writeOp = this._isWriteOperation(httpMethod); - if (serviceToken.validate(publicKey, this._allowedOffset, !writeOp) === false) { - winston.error('PrincipalAuthority:authenticate: service token validation failure: credential=' + - PrincipalToken.getUnsignedToken(signedToken)); - return null; - } + initialize() {} - /* if an authorized service signature is available then we're going to validate - * that signature as well to support token chaining in Athenz and, if necessary, - * bypass IP address mismatch for users */ - var authorizedServiceName = null; - if (serviceToken.getAuthorizedServiceSignature()) { - authorizedServiceName = this._validateAuthorizeService(serviceToken); - if (!authorizedServiceName) { - winston.error('PrincipalAuthority:authenticate: validation of authorized service failure: credential=' + - PrincipalToken.getUnsignedToken(signedToken)); + getDomain() { return null; - } } - /* if we have a usertoken and our remote ip check enabled, verify that the IP address - * matches before allowing the operation go through */ - if (userToken && !this._remoteIpCheck(remoteAddr, writeOp, serviceToken, authorizedServiceName)) { - winston.error('PrincipalAuthority:authenticate: IP Mismatch - token (' + serviceToken.getIP() + - ') request (' + remoteAddr + ')'); - return null; + getHeader() { + return this._headerName; } - /* all the role members in Athenz are normalized to lower case so we need to make - * sure our principal's name and domain are created with lower case as well */ - var princ = SimplePrincipal.createByUserIdentity(tokenDomain, tokenName, signedToken, serviceToken.getTimestamp(), this); - princ.setUnsignedCreds(serviceToken.getUnsignedToken()); - princ.setAuthorizedService(authorizedServiceName); - princ.setOriginalRequestor(serviceToken.getOriginalRequestor()); - princ.setKeyService(keyService); - princ.setIP(serviceToken.getIP()); - princ.setKeyId(serviceToken.getKeyId()); - return princ; - } - - _remoteIpCheck(remoteAddr, writeOp, serviceToken, authorizedServiceName) { - var checkResult = true; - switch (this._ipCheckMode) { - case 'OPS_ALL': - if (remoteAddr !== serviceToken.getIP()) { - checkResult = false; + authenticate(signedToken, remoteAddr, httpMethod) { + winston.debug('Authenticating PrincipalToken: ' + signedToken); + + var serviceToken = null; + try { + serviceToken = new PrincipalToken(signedToken); + } catch (e) { + winston.error( + 'PrincipalAuthority:authenticate: Invalid token: exc=' + + e.message + + ' : credential=' + + PrincipalToken.getUnsignedToken(signedToken) + ); + return null; } - break; - case 'OPS_WRITE': - /* if we have a user token for a write operation and we have an IP address - * mismatch then we'll allow this authenticate request to proceed only if it's - * been configured with authorized user only. */ - if (writeOp && remoteAddr !== serviceToken.getIP()) { - if (!authorizedServiceName) { - checkResult = false; - } + + /* before authenticating verify that if this is a valid + * authorized service token or not and if required + * components are provided (the method already logs + * all error messages) */ + if (!serviceToken.isValidAuthorizedServiceToken()) { + winston.error( + 'PrincipalAuthority:authenticate: Invalid authorized service token: credential=' + + PrincipalToken.getUnsignedToken(signedToken) + ); + return null; } - break; - case 'OPS_NONE': - break; - default: - checkResult = false; - break; - } - return checkResult; - } - - _getPublicKey(tokenDomain, tokenName, keyService, keyId, userToken) { - /* by default we're going to look for the public key for the domain - * and service defined in the token */ - var publicKeyDomain = tokenDomain; - var publicKeyService = tokenName; - - /* now let's handle the exceptions: - * 1) if the token has a key service field set then only supported values are - * either zms or zts, so we use sys.auth.zms or sys.auth.zts services - * 2) if the token's domain is user then it's a user token or if it's sd then - * it's our special project token so for those cases we are going to ask for - * zms's own public key. */ - if (keyService) { - if (keyService === ZMS_SERVICE) { - publicKeyDomain = SYS_AUTH_DOMAIN; - publicKeyService = ZMS_SERVICE; - } else if (keyService === ZTS_SERVICE) { - publicKeyDomain = SYS_AUTH_DOMAIN; - publicKeyService = ZTS_SERVICE; - } - } else if (userToken) { - publicKeyDomain = SYS_AUTH_DOMAIN; - publicKeyService = ZMS_SERVICE; - } - return this._keyStore.getPublicKey(publicKeyDomain, publicKeyService, keyId); - } + var tokenDomain = serviceToken.getDomain().toString().toLowerCase(); + var tokenName = serviceToken.getName().toString().toLowerCase(); + var keyService = serviceToken.getKeyService(); + var userToken = tokenDomain === this._userDomain; + + /* get the public key for this token to validate signature */ + var publicKey = this._getPublicKey( + tokenDomain, + tokenName, + keyService, + serviceToken.getKeyId(), + userToken + ); + + /* the validate method logs all error messages */ + var writeOp = this._isWriteOperation(httpMethod); + if ( + serviceToken.validate(publicKey, this._allowedOffset, !writeOp) === + false + ) { + winston.error( + 'PrincipalAuthority:authenticate: service token validation failure: credential=' + + PrincipalToken.getUnsignedToken(signedToken) + ); + return null; + } + + /* if an authorized service signature is available then we're going to validate + * that signature as well to support token chaining in Athenz and, if necessary, + * bypass IP address mismatch for users */ + var authorizedServiceName = null; + if (serviceToken.getAuthorizedServiceSignature()) { + authorizedServiceName = + this._validateAuthorizeService(serviceToken); + if (!authorizedServiceName) { + winston.error( + 'PrincipalAuthority:authenticate: validation of authorized service failure: credential=' + + PrincipalToken.getUnsignedToken(signedToken) + ); + return null; + } + } - _isWriteOperation(httpMethod) { - if (!httpMethod) { - return false; + /* if we have a usertoken and our remote ip check enabled, verify that the IP address + * matches before allowing the operation go through */ + if ( + userToken && + !this._remoteIpCheck( + remoteAddr, + writeOp, + serviceToken, + authorizedServiceName + ) + ) { + winston.error( + 'PrincipalAuthority:authenticate: IP Mismatch - token (' + + serviceToken.getIP() + + ') request (' + + remoteAddr + + ')' + ); + return null; + } + + /* all the role members in Athenz are normalized to lower case so we need to make + * sure our principal's name and domain are created with lower case as well */ + var princ = SimplePrincipal.createByUserIdentity( + tokenDomain, + tokenName, + signedToken, + serviceToken.getTimestamp(), + this + ); + princ.setUnsignedCreds(serviceToken.getUnsignedToken()); + princ.setAuthorizedService(authorizedServiceName); + princ.setOriginalRequestor(serviceToken.getOriginalRequestor()); + princ.setKeyService(keyService); + princ.setIP(serviceToken.getIP()); + princ.setKeyId(serviceToken.getKeyId()); + return princ; } - if (httpMethod.toString().toUpperCase() === 'PUT' || httpMethod.toString().toUpperCase() === 'POST' || httpMethod.toString().toUpperCase() === 'DELETE') { - return true; - } else { - return false; + + _remoteIpCheck(remoteAddr, writeOp, serviceToken, authorizedServiceName) { + var checkResult = true; + switch (this._ipCheckMode) { + case 'OPS_ALL': + if (remoteAddr !== serviceToken.getIP()) { + checkResult = false; + } + break; + case 'OPS_WRITE': + /* if we have a user token for a write operation and we have an IP address + * mismatch then we'll allow this authenticate request to proceed only if it's + * been configured with authorized user only. */ + if (writeOp && remoteAddr !== serviceToken.getIP()) { + if (!authorizedServiceName) { + checkResult = false; + } + } + break; + case 'OPS_NONE': + break; + default: + checkResult = false; + break; + } + return checkResult; } - } - - _getAuthorizedServiceName(authorizedServices, authorizedServiceName) { - /* if we have an authorized service name specified then it must be - * present in the authorized services list or if it's null then the - * list must contain a single element only */ - var serviceName = authorizedServiceName; - var err = null; - if (!serviceName) { - if (authorizedServices.length !== 1) { - winston.error('getAuthorizedServiceName() failed: No authorized service name specified'); - return null; - } - serviceName = authorizedServices[0]; - } else { - if (authorizedServices.indexOf(serviceName) === -1) { - winston.error('getAuthorizedServiceName() failed: Invalid authorized service name specified:' + serviceName); - return null; - } + + _getPublicKey(tokenDomain, tokenName, keyService, keyId, userToken) { + /* by default we're going to look for the public key for the domain + * and service defined in the token */ + var publicKeyDomain = tokenDomain; + var publicKeyService = tokenName; + + /* now let's handle the exceptions: + * 1) if the token has a key service field set then only supported values are + * either zms or zts, so we use sys.auth.zms or sys.auth.zts services + * 2) if the token's domain is user then it's a user token or if it's sd then + * it's our special project token so for those cases we are going to ask for + * zms's own public key. */ + if (keyService) { + if (keyService === ZMS_SERVICE) { + publicKeyDomain = SYS_AUTH_DOMAIN; + publicKeyService = ZMS_SERVICE; + } else if (keyService === ZTS_SERVICE) { + publicKeyDomain = SYS_AUTH_DOMAIN; + publicKeyService = ZTS_SERVICE; + } + } else if (userToken) { + publicKeyDomain = SYS_AUTH_DOMAIN; + publicKeyService = ZMS_SERVICE; + } + + return this._keyStore.getPublicKey( + publicKeyDomain, + publicKeyService, + keyId + ); } - return serviceName; - } - - _validateAuthorizeService(userToken) { - /* if we have an authorized service name specified then it must be - * present in the authorized services list or if it's null then the - * list must contain a single element only */ - var authorizedServiceName = userToken.getAuthorizedServiceName(); - var err = null; - if (!authorizedServiceName) { - var authorizedServices = userToken.getAuthorizedServices(); - if (!authorizedServices || authorizedServices.length !== 1) { - winston.error('PrincipalAuthority:validateAuthorizeService: ' + - 'No service name and services list empty OR contains multiple entries: token=' + userToken.getUnsignedToken()); - return null; - } else { - authorizedServiceName = authorizedServices[0]; - } + + _isWriteOperation(httpMethod) { + if (!httpMethod) { + return false; + } + if ( + httpMethod.toString().toUpperCase() === 'PUT' || + httpMethod.toString().toUpperCase() === 'POST' || + httpMethod.toString().toUpperCase() === 'DELETE' + ) { + return true; + } else { + return false; + } } - /* need to extract domain and service name from our full service name value */ - var idx = authorizedServiceName.lastIndexOf('.'); - if (idx <= 0 || idx === authorizedServiceName.length - 1) { - winston.error('PrincipalAuthority:validateAuthorizeService: ' + - 'failed: token=' + userToken.getUnsignedToken() + - ' : Invalid authorized service name specified=' + authorizedServiceName); - return null; + _getAuthorizedServiceName(authorizedServices, authorizedServiceName) { + /* if we have an authorized service name specified then it must be + * present in the authorized services list or if it's null then the + * list must contain a single element only */ + var serviceName = authorizedServiceName; + var err = null; + if (!serviceName) { + if (authorizedServices.length !== 1) { + winston.error( + 'getAuthorizedServiceName() failed: No authorized service name specified' + ); + return null; + } + serviceName = authorizedServices[0]; + } else { + if (authorizedServices.indexOf(serviceName) === -1) { + winston.error( + 'getAuthorizedServiceName() failed: Invalid authorized service name specified:' + + serviceName + ); + return null; + } + } + return serviceName; } - var publicKey = this._keyStore.getPublicKey(authorizedServiceName.substring(0, idx), authorizedServiceName.substring(idx + 1), userToken.getAuthorizedServiceKeyId()); + _validateAuthorizeService(userToken) { + /* if we have an authorized service name specified then it must be + * present in the authorized services list or if it's null then the + * list must contain a single element only */ + var authorizedServiceName = userToken.getAuthorizedServiceName(); + var err = null; + if (!authorizedServiceName) { + var authorizedServices = userToken.getAuthorizedServices(); + if (!authorizedServices || authorizedServices.length !== 1) { + winston.error( + 'PrincipalAuthority:validateAuthorizeService: ' + + 'No service name and services list empty OR contains multiple entries: token=' + + userToken.getUnsignedToken() + ); + return null; + } else { + authorizedServiceName = authorizedServices[0]; + } + } - /* the token method reports all error messages */ - if (!userToken.validateForAuthorizedService(publicKey)) { - winston.error('PrincipalAuthority:validateAuthorizeService: token validation for authorized service failed'); - return null; + /* need to extract domain and service name from our full service name value */ + var idx = authorizedServiceName.lastIndexOf('.'); + if (idx <= 0 || idx === authorizedServiceName.length - 1) { + winston.error( + 'PrincipalAuthority:validateAuthorizeService: ' + + 'failed: token=' + + userToken.getUnsignedToken() + + ' : Invalid authorized service name specified=' + + authorizedServiceName + ); + return null; + } + + var publicKey = this._keyStore.getPublicKey( + authorizedServiceName.substring(0, idx), + authorizedServiceName.substring(idx + 1), + userToken.getAuthorizedServiceKeyId() + ); + + /* the token method reports all error messages */ + if (!userToken.validateForAuthorizedService(publicKey)) { + winston.error( + 'PrincipalAuthority:validateAuthorizeService: token validation for authorized service failed' + ); + return null; + } + return authorizedServiceName; } - return authorizedServiceName; - } - setKeyStore(keyStore) { - this._keyStore = keyStore; - } + setKeyStore(keyStore) { + this._keyStore = keyStore; + } } module.exports = PrincipalAuthority; diff --git a/libs/nodejs/auth_core/src/impl/RoleAuthority.js b/libs/nodejs/auth_core/src/impl/RoleAuthority.js index ed2ea75ef59..8eef6826600 100644 --- a/libs/nodejs/auth_core/src/impl/RoleAuthority.js +++ b/libs/nodejs/auth_core/src/impl/RoleAuthority.js @@ -27,107 +27,142 @@ var ATHENZ_PROP_USER_DOMAIN; var ATHENZ_PROP_ROLE_HEADER; class RoleAuthority { - constructor() { - winston.level = config.logLevel; - - ATHENZ_PROP_TOKEN_OFFSET = Number(config.roleTokenAllowedOffset); - ATHENZ_PROP_USER_DOMAIN = config.roleUserDomain; - ATHENZ_PROP_ROLE_HEADER = config.roleHeader; - - this._keyStore = null; - this._allowedOffset = (ATHENZ_PROP_TOKEN_OFFSET) ? Number(ATHENZ_PROP_TOKEN_OFFSET) : 300; - this._userDomain = ATHENZ_PROP_USER_DOMAIN || 'user'; - this._headerName = ATHENZ_PROP_ROLE_HEADER || 'Athenz-Role-Auth'; - - // case of invalid value, we'll default back to 5 minutes - if (this._allowedOffset < 0) { - this._allowedOffset = 300; - } - } - - static setConfig(c) { - config = Object.assign({}, config, c.auth_core); - RoleToken.setConfig(c); - SimplePrincipal.setConfig(c); - } - - initialize() { - } - - getDomain() { - return 'sys.auth'; - } - - getHeader() { - return this._headerName; - } - - authenticate(signedToken, remoteAddr, httpMethod) { - winston.debug('Authenticating PrincipalToken: ' + signedToken); - - var roleToken = null; - try { - roleToken = new RoleToken(signedToken); - } catch (e) { - winston.error('PrincipalAuthority:authenticate: Invalid token: exc=' + e.message + - ' : credential=' + RoleToken.getUnsignedToken(signedToken)); - return null; + constructor() { + winston.level = config.logLevel; + + ATHENZ_PROP_TOKEN_OFFSET = Number(config.roleTokenAllowedOffset); + ATHENZ_PROP_USER_DOMAIN = config.roleUserDomain; + ATHENZ_PROP_ROLE_HEADER = config.roleHeader; + + this._keyStore = null; + this._allowedOffset = ATHENZ_PROP_TOKEN_OFFSET + ? Number(ATHENZ_PROP_TOKEN_OFFSET) + : 300; + this._userDomain = ATHENZ_PROP_USER_DOMAIN || 'user'; + this._headerName = ATHENZ_PROP_ROLE_HEADER || 'Athenz-Role-Auth'; + + // case of invalid value, we'll default back to 5 minutes + if (this._allowedOffset < 0) { + this._allowedOffset = 300; + } } - /* if the token's domain is user then we need to check to see if this is a write - * operation (PUT/POST/DELETE) and in that case we must validate the IP - * address of the incoming request to make sure it matches to IP address - * that's stored in the RoleToken */ - if (remoteAddr !== roleToken.getIP() && this._isWriteOperation(httpMethod)) { - var tokenPrincipal = roleToken.getPrincipal(); - var idx = tokenPrincipal.lastIndexOf('.'); - - if (idx <= 0 || idx === tokenPrincipal.length - 1) { - winston.error('RoleAuthority:authenticate failed: Invalid principal specified: ' + - tokenPrincipal + ': credential=' + - RoleToken.getUnsignedToken(signedToken)); - return null; - } - - if (tokenPrincipal.substring(0, idx).toLowerCase() === this._userDomain) { - winston.error('RoleAuthority:authenticate failed: IP Mismatch - tokenip(' + - roleToken.getIP() + ') request-addr(' + - remoteAddr + ') credential=' + - RoleToken.getUnsignedToken(signedToken)); - return null; - } + static setConfig(c) { + config = Object.assign({}, config, c.auth_core); + RoleToken.setConfig(c); + SimplePrincipal.setConfig(c); } - var publicKey = this._keyStore.getPublicKey(SYS_AUTH_DOMAIN, ZTS_SERVICE, roleToken.getKeyId()); + initialize() {} - if (roleToken.validate(publicKey, this._allowedOffset, false) === false) { - winston.error('RoleAuthority:authenticate failed: validation was not successful: credential=' + - RoleToken.getUnsignedToken(signedToken)); - return null; + getDomain() { + return 'sys.auth'; } - // all the role members in Athenz are normalized to lower case so we need to make - // sure our principal's name and domain are created with lower case as well - - var princ = SimplePrincipal.createByRoles(roleToken.getDomain(), signedToken, roleToken.getRoles(), this); - princ.setUnsignedCreds(roleToken.getUnsignedToken()); - return princ; - } + getHeader() { + return this._headerName; + } - _isWriteOperation(httpMethod) { - if (!httpMethod) { - return false; + authenticate(signedToken, remoteAddr, httpMethod) { + winston.debug('Authenticating PrincipalToken: ' + signedToken); + + var roleToken = null; + try { + roleToken = new RoleToken(signedToken); + } catch (e) { + winston.error( + 'PrincipalAuthority:authenticate: Invalid token: exc=' + + e.message + + ' : credential=' + + RoleToken.getUnsignedToken(signedToken) + ); + return null; + } + + /* if the token's domain is user then we need to check to see if this is a write + * operation (PUT/POST/DELETE) and in that case we must validate the IP + * address of the incoming request to make sure it matches to IP address + * that's stored in the RoleToken */ + if ( + remoteAddr !== roleToken.getIP() && + this._isWriteOperation(httpMethod) + ) { + var tokenPrincipal = roleToken.getPrincipal(); + var idx = tokenPrincipal.lastIndexOf('.'); + + if (idx <= 0 || idx === tokenPrincipal.length - 1) { + winston.error( + 'RoleAuthority:authenticate failed: Invalid principal specified: ' + + tokenPrincipal + + ': credential=' + + RoleToken.getUnsignedToken(signedToken) + ); + return null; + } + + if ( + tokenPrincipal.substring(0, idx).toLowerCase() === + this._userDomain + ) { + winston.error( + 'RoleAuthority:authenticate failed: IP Mismatch - tokenip(' + + roleToken.getIP() + + ') request-addr(' + + remoteAddr + + ') credential=' + + RoleToken.getUnsignedToken(signedToken) + ); + return null; + } + } + + var publicKey = this._keyStore.getPublicKey( + SYS_AUTH_DOMAIN, + ZTS_SERVICE, + roleToken.getKeyId() + ); + + if ( + roleToken.validate(publicKey, this._allowedOffset, false) === false + ) { + winston.error( + 'RoleAuthority:authenticate failed: validation was not successful: credential=' + + RoleToken.getUnsignedToken(signedToken) + ); + return null; + } + + // all the role members in Athenz are normalized to lower case so we need to make + // sure our principal's name and domain are created with lower case as well + + var princ = SimplePrincipal.createByRoles( + roleToken.getDomain(), + signedToken, + roleToken.getRoles(), + this + ); + princ.setUnsignedCreds(roleToken.getUnsignedToken()); + return princ; } - if (httpMethod.toString().toUpperCase() === 'PUT' || httpMethod.toString().toUpperCase() === 'POST' || httpMethod.toString().toUpperCase() === 'DELETE') { - return true; - } else { - return false; + + _isWriteOperation(httpMethod) { + if (!httpMethod) { + return false; + } + if ( + httpMethod.toString().toUpperCase() === 'PUT' || + httpMethod.toString().toUpperCase() === 'POST' || + httpMethod.toString().toUpperCase() === 'DELETE' + ) { + return true; + } else { + return false; + } } - } - setKeyStore(keyStore) { - this._keyStore = keyStore; - } + setKeyStore(keyStore) { + this._keyStore = keyStore; + } } module.exports = RoleAuthority; diff --git a/libs/nodejs/auth_core/src/impl/SimplePrincipal.js b/libs/nodejs/auth_core/src/impl/SimplePrincipal.js index ac6b9875536..98246551c97 100644 --- a/libs/nodejs/auth_core/src/impl/SimplePrincipal.js +++ b/libs/nodejs/auth_core/src/impl/SimplePrincipal.js @@ -18,219 +18,245 @@ var Validate = require('../util/Validate'); var config = require('../../config/config')(); class SimplePrincipal { - /*eslint max-params: ["error", 6]*/ - constructor(domain, name, creds, roles, issueTime,authority) { - winston.level = config.logLevel; - - this._domain = domain; - this._name = name; - this._creds = creds; - this._roles = roles; - this._issueTime = Number(issueTime); - this._authority = authority; - this._fullName = null; - this._unsignedCreds = null; - this._ip = null; - this._authorizedService = null; - this._originalRequestor = null; - this._keyService = null; - this._keyId = null; - this._x509Certificate = null; - } - - static setConfig(c) { - config = Object.assign({}, config, c.auth_core); - } - - static _simplePrincipal(domain, name, creds, issueTime, authority) { - return new SimplePrincipal(domain, name, creds, null, issueTime, authority); - } - - static _simplePrincipalByRoles(domain, creds, roles, authority) { - return new SimplePrincipal(domain, null, creds, roles, 0, authority); - } - - static create(domain, name, creds) { - return this.createByUserIdentity(domain, name, creds, 0, null); - } - - /** - * Create a Principal based on a given RoleToken - * @param domain Domain name that the RoleToken was issued for - * @param creds Credentials of the principal (RoleToken) - * @param roles List of roles defined in the token - * @param authority authority responsible for the credentials (RoleAuthority) - * @return a Principal for the given set of roles in a domain - */ - static createByRoles(domain, creds, roles, authority) { - if (!Validate.domainName(domain)) { - winston.warn('createByRoles: failed to validate domain ' + domain); - } - - if (!roles || roles.length === 0) { - winston.warn('zero roles: ' + creds); - } - - return this._simplePrincipalByRoles(domain, creds, roles, authority); - } - - /** - * Create a Principal for the given identity - * @param domain Domain name for the identity - * @param name Name of the identity - * @param creds Credentials of the principal (PrincipalToken which could be either UserToken or ServiceToken) - * @param authority authority responsible for the credentials (e.g. PrincipalAuthority) - * @return a Principal for the identity - */ - static createByIdentity(domain, name, creds, authority) { - return this.createByUserIdentity(domain, name, creds, '0', authority); - } - - /** - * Create a Principal for the given user identity - * @param domain Domain name for the identity (For users this will always be user) - * @param name Name of the identity - * @param creds Credentials of the principal (e.g. Cookie.User) - * @param issueTime when the User Cookie/Credentials was issued - * @param authority authority responsible for the credentials (e.g. UserAuthority) - * @return a Principal for the identity - */ - static createByUserIdentity(domain, name, creds, issueTime, authority) { - if (!Validate.domainName(domain)) { - winston.warn('createByUserIdentity: failed to validate domain ' + domain); - } - - if (!Validate.principalName(name)) { - winston.warn('createByUserIdentity: failed to validate name ' + name); - } - - if (domain) { - var matchDomain = (!authority) ? null : authority.getDomain(); - if (matchDomain && domain !== matchDomain) { - winston.warn('domain mismatch for user ' + name + ' in authority + ' + authority); - return null; - } - } else if (authority) { - if (authority.getDomain()) { - winston.warn('domain mismatch for user ' + name + ' in authority + ' + authority); - return null; - } - } - - return this._simplePrincipal(domain, name, creds, issueTime, authority); - } - - /** - * Create a Principal for the given host identity - * @param appId Application identifer - * @param creds Credentials of the principal - * @param authority authority responsible for the credentials (e.g. HostAuthority) - * @return a Principal for the host identity - */ - static createByHostIdentity(appId, creds, authority) { - return this._simplePrincipal(null, appId, creds, 0, authority); - } - - setUnsignedCreds(unsignedCreds) { - this._unsignedCreds = unsignedCreds; - } - - setAuthorizedService(authorizedService) { - this._authorizedService = authorizedService; - } - - setIP(ip) { - this._ip = ip; - } - - setOriginalRequestor(originalRequestor) { - this._originalRequestor = originalRequestor; - } - - setKeyService(keyService) { - this._keyService = keyService; - } - - setKeyId(keyId) { - this._keyId = keyId; - } - - setX509Certificate(x509Certificate) { - this._x509Certificate = x509Certificate; - } - - getIP() { - return this._ip; - } - - getUnsignedCreds() { - return this._unsignedCreds; - } - - getAuthority() { - return this._authority; - } - - getDomain() { - return this._domain; - } - - getName() { - return this._name; - } - - getOriginalRequestor() { - return this._originalRequestor; - } - - getFullName() { - if (!this._fullName) { - if (this._domain && this._name) { - this._fullName = this._domain + '.' + this._name; - } else if (this._domain) { - this._fullName = this._domain; - } else if (this._name) { - this._fullName = this._name; - } - } - return this._fullName; - } - - getCredentials() { - return this._creds; - } - - getRoles() { - return this._roles; - } - - getIssueTime() { - return this._issueTime; - } - - toString() { - if (!this._roles) { - return this._domain + '.' + this._name; - } else { - return 'ZToken_' + this._domain + '~' + this._roles.toString().replace('[', '').replace(']', ''); - } - } - - getAuthorizedService() { - return this._authorizedService; - } - - getKeyService() { - return this._keyService; - } - - getKeyId() { - return this._keyId; - } - - getX509Certificate() { - return this._x509Certificate; - } + /*eslint max-params: ["error", 6]*/ + constructor(domain, name, creds, roles, issueTime, authority) { + winston.level = config.logLevel; + + this._domain = domain; + this._name = name; + this._creds = creds; + this._roles = roles; + this._issueTime = Number(issueTime); + this._authority = authority; + this._fullName = null; + this._unsignedCreds = null; + this._ip = null; + this._authorizedService = null; + this._originalRequestor = null; + this._keyService = null; + this._keyId = null; + this._x509Certificate = null; + } + + static setConfig(c) { + config = Object.assign({}, config, c.auth_core); + } + + static _simplePrincipal(domain, name, creds, issueTime, authority) { + return new SimplePrincipal( + domain, + name, + creds, + null, + issueTime, + authority + ); + } + + static _simplePrincipalByRoles(domain, creds, roles, authority) { + return new SimplePrincipal(domain, null, creds, roles, 0, authority); + } + + static create(domain, name, creds) { + return this.createByUserIdentity(domain, name, creds, 0, null); + } + + /** + * Create a Principal based on a given RoleToken + * @param domain Domain name that the RoleToken was issued for + * @param creds Credentials of the principal (RoleToken) + * @param roles List of roles defined in the token + * @param authority authority responsible for the credentials (RoleAuthority) + * @return a Principal for the given set of roles in a domain + */ + static createByRoles(domain, creds, roles, authority) { + if (!Validate.domainName(domain)) { + winston.warn('createByRoles: failed to validate domain ' + domain); + } + + if (!roles || roles.length === 0) { + winston.warn('zero roles: ' + creds); + } + + return this._simplePrincipalByRoles(domain, creds, roles, authority); + } + + /** + * Create a Principal for the given identity + * @param domain Domain name for the identity + * @param name Name of the identity + * @param creds Credentials of the principal (PrincipalToken which could be either UserToken or ServiceToken) + * @param authority authority responsible for the credentials (e.g. PrincipalAuthority) + * @return a Principal for the identity + */ + static createByIdentity(domain, name, creds, authority) { + return this.createByUserIdentity(domain, name, creds, '0', authority); + } + + /** + * Create a Principal for the given user identity + * @param domain Domain name for the identity (For users this will always be user) + * @param name Name of the identity + * @param creds Credentials of the principal (e.g. Cookie.User) + * @param issueTime when the User Cookie/Credentials was issued + * @param authority authority responsible for the credentials (e.g. UserAuthority) + * @return a Principal for the identity + */ + static createByUserIdentity(domain, name, creds, issueTime, authority) { + if (!Validate.domainName(domain)) { + winston.warn( + 'createByUserIdentity: failed to validate domain ' + domain + ); + } + + if (!Validate.principalName(name)) { + winston.warn( + 'createByUserIdentity: failed to validate name ' + name + ); + } + + if (domain) { + var matchDomain = !authority ? null : authority.getDomain(); + if (matchDomain && domain !== matchDomain) { + winston.warn( + 'domain mismatch for user ' + + name + + ' in authority + ' + + authority + ); + return null; + } + } else if (authority) { + if (authority.getDomain()) { + winston.warn( + 'domain mismatch for user ' + + name + + ' in authority + ' + + authority + ); + return null; + } + } + + return this._simplePrincipal(domain, name, creds, issueTime, authority); + } + + /** + * Create a Principal for the given host identity + * @param appId Application identifer + * @param creds Credentials of the principal + * @param authority authority responsible for the credentials (e.g. HostAuthority) + * @return a Principal for the host identity + */ + static createByHostIdentity(appId, creds, authority) { + return this._simplePrincipal(null, appId, creds, 0, authority); + } + + setUnsignedCreds(unsignedCreds) { + this._unsignedCreds = unsignedCreds; + } + + setAuthorizedService(authorizedService) { + this._authorizedService = authorizedService; + } + + setIP(ip) { + this._ip = ip; + } + + setOriginalRequestor(originalRequestor) { + this._originalRequestor = originalRequestor; + } + + setKeyService(keyService) { + this._keyService = keyService; + } + + setKeyId(keyId) { + this._keyId = keyId; + } + + setX509Certificate(x509Certificate) { + this._x509Certificate = x509Certificate; + } + + getIP() { + return this._ip; + } + + getUnsignedCreds() { + return this._unsignedCreds; + } + + getAuthority() { + return this._authority; + } + + getDomain() { + return this._domain; + } + + getName() { + return this._name; + } + + getOriginalRequestor() { + return this._originalRequestor; + } + + getFullName() { + if (!this._fullName) { + if (this._domain && this._name) { + this._fullName = this._domain + '.' + this._name; + } else if (this._domain) { + this._fullName = this._domain; + } else if (this._name) { + this._fullName = this._name; + } + } + return this._fullName; + } + + getCredentials() { + return this._creds; + } + + getRoles() { + return this._roles; + } + + getIssueTime() { + return this._issueTime; + } + + toString() { + if (!this._roles) { + return this._domain + '.' + this._name; + } else { + return ( + 'ZToken_' + + this._domain + + '~' + + this._roles.toString().replace('[', '').replace(']', '') + ); + } + } + + getAuthorizedService() { + return this._authorizedService; + } + + getKeyService() { + return this._keyService; + } + + getKeyId() { + return this._keyId; + } + + getX509Certificate() { + return this._x509Certificate; + } } module.exports = SimplePrincipal; diff --git a/libs/nodejs/auth_core/src/impl/SimpleServiceIdentityProvider.js b/libs/nodejs/auth_core/src/impl/SimpleServiceIdentityProvider.js index 5e4ced82691..640dacafb58 100644 --- a/libs/nodejs/auth_core/src/impl/SimpleServiceIdentityProvider.js +++ b/libs/nodejs/auth_core/src/impl/SimpleServiceIdentityProvider.js @@ -20,83 +20,89 @@ var SimplePrincipal = require('./SimplePrincipal'); var os = require('os'); class SimpleServiceIdentityProvider { - /** - * A simple implementation of the ServiceIdentityProvider interface. - * The caller specifies the domain and service name along with the - * private key for the given service - * @param domainName Name of the domain - * @param serviceName Name of the service - * @param privateKey the private key for the service - * @param keyId the registered key id in ZMS for this private key - */ - constructor(domainName, serviceName, privateKey, keyId) { - this._Authority = new PrincipalAuthority(); - - this._domain = domainName.toString().toLowerCase(); - this._service = serviceName.toString().toLowerCase(); - this._key = privateKey; - this._tokenTimeout = 3600; - this._keyId = keyId.toString().toLowerCase(); - this.setHost(this._getServerHostName()); - } - - static setConfig(c) { - PrincipalAuthority.setConfig(c); - PrincipalToken.setConfig(c); - SimplePrincipal.setConfig(c); - } - - getIdentity(domainName, serviceName) { - // all the role members in Athenz are normalized to lower case so we need to make - // sure our principal's name and domain are created with lower case as well - domainName = domainName.toString().toLowerCase(); - serviceName = serviceName.toString().toLowerCase(); - - // make sure we're handling correct domain and service - if (domainName !== this._domain || serviceName !== this._service) { - return null; + /** + * A simple implementation of the ServiceIdentityProvider interface. + * The caller specifies the domain and service name along with the + * private key for the given service + * @param domainName Name of the domain + * @param serviceName Name of the service + * @param privateKey the private key for the service + * @param keyId the registered key id in ZMS for this private key + */ + constructor(domainName, serviceName, privateKey, keyId) { + this._Authority = new PrincipalAuthority(); + + this._domain = domainName.toString().toLowerCase(); + this._service = serviceName.toString().toLowerCase(); + this._key = privateKey; + this._tokenTimeout = 3600; + this._keyId = keyId.toString().toLowerCase(); + this.setHost(this._getServerHostName()); } - var tokenObj = { - version: 'S1', - domain: domainName, - name: serviceName, - expirationWindow: this._tokenTimeout, - host: this._host, - keyId: this._keyId - }; - - var token = new PrincipalToken(tokenObj); - token.sign(this._key); - - var principal = SimplePrincipal.createByUserIdentity(domainName, serviceName, token.getSignedToken(), Math.floor(Date.now() / 1000), this._Authority); - principal.setUnsignedCreds(token.getUnsignedToken()); - return principal; - } - - _getServerHostName() { - var urlhost = null; - - try { - urlhost = os.hostname(); - } catch (e) { - urlhost = 'localhost'; + static setConfig(c) { + PrincipalAuthority.setConfig(c); + PrincipalToken.setConfig(c); + SimplePrincipal.setConfig(c); } - return urlhost; - } + getIdentity(domainName, serviceName) { + // all the role members in Athenz are normalized to lower case so we need to make + // sure our principal's name and domain are created with lower case as well + domainName = domainName.toString().toLowerCase(); + serviceName = serviceName.toString().toLowerCase(); + + // make sure we're handling correct domain and service + if (domainName !== this._domain || serviceName !== this._service) { + return null; + } + + var tokenObj = { + version: 'S1', + domain: domainName, + name: serviceName, + expirationWindow: this._tokenTimeout, + host: this._host, + keyId: this._keyId, + }; + + var token = new PrincipalToken(tokenObj); + token.sign(this._key); + + var principal = SimplePrincipal.createByUserIdentity( + domainName, + serviceName, + token.getSignedToken(), + Math.floor(Date.now() / 1000), + this._Authority + ); + principal.setUnsignedCreds(token.getUnsignedToken()); + return principal; + } + + _getServerHostName() { + var urlhost = null; + + try { + urlhost = os.hostname(); + } catch (e) { + urlhost = 'localhost'; + } + + return urlhost; + } - getHost() { - return this._host; - } + getHost() { + return this._host; + } - setHost(host) { - this._host = host; - } + setHost(host) { + this._host = host; + } - setTokenTimeout(tokenTimeout) { - this._tokenTimeout = tokenTimeout; - } + setTokenTimeout(tokenTimeout) { + this._tokenTimeout = tokenTimeout; + } } module.exports = SimpleServiceIdentityProvider; diff --git a/libs/nodejs/auth_core/src/token/PrincipalToken.js b/libs/nodejs/auth_core/src/token/PrincipalToken.js index d232b640382..2c70843906e 100644 --- a/libs/nodejs/auth_core/src/token/PrincipalToken.js +++ b/libs/nodejs/auth_core/src/token/PrincipalToken.js @@ -19,363 +19,450 @@ var Crypto = require('../util/Crypto'); var config = require('../../config/config')(); class PrincipalToken extends Token { - constructor(token) { - super(); - - winston.level = config.logLevel; - - this._name = null; - this._originalRequestor = null; - this._keyService = null; - this._authorizedServices = null; - this._authorizedServiceName = null; - this._authorizedServiceKeyId = '0'; - this._authorizedServiceSignature = null; - - if (typeof token === 'string') { - try { - this.parseSignedToken(token); - } catch (e) { - throw e; - } - } else { - try { - this.builder(token); - } catch (e) { - throw e; - } + constructor(token) { + super(); + + winston.level = config.logLevel; + + this._name = null; + this._originalRequestor = null; + this._keyService = null; + this._authorizedServices = null; + this._authorizedServiceName = null; + this._authorizedServiceKeyId = '0'; + this._authorizedServiceSignature = null; + + if (typeof token === 'string') { + try { + this.parseSignedToken(token); + } catch (e) { + throw e; + } + } else { + try { + this.builder(token); + } catch (e) { + throw e; + } + } } - } - static setConfig(c) { - super.setConfig(c); - config = Object.assign({}, config, c.auth_core); - } + static setConfig(c) { + super.setConfig(c); + config = Object.assign({}, config, c.auth_core); + } - /*eslint complexity: ["error", 24]*/ - parseSignedToken(signedToken) { - winston.debug('Constructing PrincipalToken with input string: ' + signedToken); + /*eslint complexity: ["error", 24]*/ + parseSignedToken(signedToken) { + winston.debug( + 'Constructing PrincipalToken with input string: ' + signedToken + ); - if (!signedToken) { - throw new Error('Input String signedToken must not be empty'); - } + if (!signedToken) { + throw new Error('Input String signedToken must not be empty'); + } - /** - * first we need to extract data and signature parts - * the signature is always at the end of the token. The principal - * token can represent 2 types - service or user. The version - * string identifies the type by using S or U. Here are two sample - * tokens: - * - * User: - * v=U1;d=user;n=john;a=salt;t=tstamp;e=expiry;s=sig - * - * Service: - * v=S1;d=sports;n=storage;h=host.somecompany.com;a=salt;t=tstamp;e=expiry;s=sig - * - * v: version number U1 or S1 (string) - * d: domain name (as passed by the client) or user for users - * n: service name (as passed by the client) or username - * h: hostname or IP address (string) - * a: random 8 byte salt value hex encoded - * t: timestamp when the token was generated - * e: expiry timestamp based on SIA configuration - * s: signature generated over the "v=U1;a=salt;...;e=expiry" string - * using Service's private Key for service tokens and ZMS service's - * private key for user tokens and y64 encoded - */ - var idx = signedToken.indexOf(';s='); - if (idx !== -1) { - this._unsignedToken = signedToken.substring(0, idx); - } + /** + * first we need to extract data and signature parts + * the signature is always at the end of the token. The principal + * token can represent 2 types - service or user. The version + * string identifies the type by using S or U. Here are two sample + * tokens: + * + * User: + * v=U1;d=user;n=john;a=salt;t=tstamp;e=expiry;s=sig + * + * Service: + * v=S1;d=sports;n=storage;h=host.somecompany.com;a=salt;t=tstamp;e=expiry;s=sig + * + * v: version number U1 or S1 (string) + * d: domain name (as passed by the client) or user for users + * n: service name (as passed by the client) or username + * h: hostname or IP address (string) + * a: random 8 byte salt value hex encoded + * t: timestamp when the token was generated + * e: expiry timestamp based on SIA configuration + * s: signature generated over the "v=U1;a=salt;...;e=expiry" string + * using Service's private Key for service tokens and ZMS service's + * private key for user tokens and y64 encoded + */ + var idx = signedToken.indexOf(';s='); + if (idx !== -1) { + this._unsignedToken = signedToken.substring(0, idx); + } - var item = signedToken.split(';'); - for (var i = 0; i < item.length; i++) { - var kv = item[i].split('='); - if (kv.length === 2) { - switch (kv[0]) { - case 'a': - this._salt = kv[1]; - break; - case 'b': - this._authorizedServices = kv[1].split(','); - break; - case 'bk': - this._authorizedServiceKeyId = kv[1]; - break; - case 'bn': - this._authorizedServiceName = kv[1]; - break; - case 'bs': - this._authorizedServiceSignature = kv[1]; - break; - case 'd': - this._domain = kv[1]; - break; - case 'e': - this._expiryTime = Number(kv[1]); - break; - case 'h': - this._host = kv[1]; - break; - case 'i': - this._ip = kv[1]; - break; - case 'k': - this._keyId = kv[1]; - break; - case 'n': - this._name = kv[1]; - break; - case 'o': - this._originalRequestor = kv[1]; - break; - case 's': - this._signature = kv[1]; - break; - case 't': - this._timestamp = Number(kv[1]); - break; - case 'v': - this._version = kv[1]; - break; - case 'z': - this._keyService = kv[1]; - break; + var item = signedToken.split(';'); + for (var i = 0; i < item.length; i++) { + var kv = item[i].split('='); + if (kv.length === 2) { + switch (kv[0]) { + case 'a': + this._salt = kv[1]; + break; + case 'b': + this._authorizedServices = kv[1].split(','); + break; + case 'bk': + this._authorizedServiceKeyId = kv[1]; + break; + case 'bn': + this._authorizedServiceName = kv[1]; + break; + case 'bs': + this._authorizedServiceSignature = kv[1]; + break; + case 'd': + this._domain = kv[1]; + break; + case 'e': + this._expiryTime = Number(kv[1]); + break; + case 'h': + this._host = kv[1]; + break; + case 'i': + this._ip = kv[1]; + break; + case 'k': + this._keyId = kv[1]; + break; + case 'n': + this._name = kv[1]; + break; + case 'o': + this._originalRequestor = kv[1]; + break; + case 's': + this._signature = kv[1]; + break; + case 't': + this._timestamp = Number(kv[1]); + break; + case 'v': + this._version = kv[1]; + break; + case 'z': + this._keyService = kv[1]; + break; + } + } } - } - } - /* the required attributes for the token are - * domain and roles. The signature will be verified - * during the authenticate phase but now we'll make - * sure that domain and roles are present - */ + /* the required attributes for the token are + * domain and roles. The signature will be verified + * during the authenticate phase but now we'll make + * sure that domain and roles are present + */ - if (!this._domain) { - throw new Error('SignedToken does not contain required domain component'); - } + if (!this._domain) { + throw new Error( + 'SignedToken does not contain required domain component' + ); + } - if (!this._name) { - throw new Error('SignedToken does not contain required name component'); - } + if (!this._name) { + throw new Error( + 'SignedToken does not contain required name component' + ); + } - this._signedToken = signedToken; - - winston.debug( - 'Values extracted from token ' + - ' version:' + this._version + - ' domain:' + this._domain + - ' service:' + this._name + - ' host:' + this._host + - ' ip: ' + this._ip + - ' id: ' + this._keyId + - ' keyService: ' + this._keyService + - ' originalRequestor: ' + this._originalRequestor + - ' salt:' + this._salt + - ' timestamp:' + this._timestamp + - ' expiryTime:' + this._expiryTime + - ' signature:' + this._signature); - if (this._authorizedServices) { - winston.debug( - 'Authorized service details from token ' + - ' authorizedServices:' + this._authorizedServices.join(',') + - ' authorizedServiceName:' + this._authorizedServiceName + - ' authorizedServiceKeyId:' + this._authorizedServiceKeyId + - ' authorizedServiceSignature:' + this._authorizedServiceSignature - ); + this._signedToken = signedToken; + + winston.debug( + 'Values extracted from token ' + + ' version:' + + this._version + + ' domain:' + + this._domain + + ' service:' + + this._name + + ' host:' + + this._host + + ' ip: ' + + this._ip + + ' id: ' + + this._keyId + + ' keyService: ' + + this._keyService + + ' originalRequestor: ' + + this._originalRequestor + + ' salt:' + + this._salt + + ' timestamp:' + + this._timestamp + + ' expiryTime:' + + this._expiryTime + + ' signature:' + + this._signature + ); + if (this._authorizedServices) { + winston.debug( + 'Authorized service details from token ' + + ' authorizedServices:' + + this._authorizedServices.join(',') + + ' authorizedServiceName:' + + this._authorizedServiceName + + ' authorizedServiceKeyId:' + + this._authorizedServiceKeyId + + ' authorizedServiceSignature:' + + this._authorizedServiceSignature + ); + } } - } - builder(options) { - if (!options.version || !options.domain || !options.name) { - throw new Error('version, domain and name parameters must not be null.'); - } + builder(options) { + if (!options.version || !options.domain || !options.name) { + throw new Error( + 'version, domain and name parameters must not be null.' + ); + } - this._version = options.version; - this._domain = options.domain; - this._name = options.name; - this._host = options.host; - this._salt = (options.salt || Crypto.randomSalt()); - this._keyId = (options.keyId || '0'); - this._ip = options.ip; - this._authorizedServices = (options.authorizedServices) ? options.authorizedServices.split(',') : null; - this._keyService = options.keyService; - this._originalRequestor = options.originalRequestor; - - this.setTimeStamp((options.issueTime || 0), (options.expirationWindow || 3600)); - - var parts = []; - parts.push('v=' + this._version); - parts.push('d=' + this._domain); - parts.push('n=' + this._name); - if (this._host) { - parts.push('h=' + this._host); - } - parts.push('a=' + this._salt); - parts.push('t=' + this._timestamp); - parts.push('e=' + this._expiryTime); - parts.push('k=' + this._keyId); - if (this._keyService) { - parts.push('z=' + this._keyService); - } - if (this._originalRequestor) { - parts.push('o=' + this._originalRequestor); - } - if (this._ip) { - parts.push('i=' + this._ip); - } - if (this._authorizedServices) { - parts.push('b=' + this._authorizedServices.join(',')); + this._version = options.version; + this._domain = options.domain; + this._name = options.name; + this._host = options.host; + this._salt = options.salt || Crypto.randomSalt(); + this._keyId = options.keyId || '0'; + this._ip = options.ip; + this._authorizedServices = options.authorizedServices + ? options.authorizedServices.split(',') + : null; + this._keyService = options.keyService; + this._originalRequestor = options.originalRequestor; + + this.setTimeStamp( + options.issueTime || 0, + options.expirationWindow || 3600 + ); + + var parts = []; + parts.push('v=' + this._version); + parts.push('d=' + this._domain); + parts.push('n=' + this._name); + if (this._host) { + parts.push('h=' + this._host); + } + parts.push('a=' + this._salt); + parts.push('t=' + this._timestamp); + parts.push('e=' + this._expiryTime); + parts.push('k=' + this._keyId); + if (this._keyService) { + parts.push('z=' + this._keyService); + } + if (this._originalRequestor) { + parts.push('o=' + this._originalRequestor); + } + if (this._ip) { + parts.push('i=' + this._ip); + } + if (this._authorizedServices) { + parts.push('b=' + this._authorizedServices.join(',')); + } + this._unsignedToken = parts.join(';'); + winston.debug('PrincipalToken created: ' + this._unsignedToken); } - this._unsignedToken = parts.join(';'); - winston.debug('PrincipalToken created: ' + this._unsignedToken); - } - signForAuthorizedService(authorizedServiceName, authorizedServiceKeyId, privateKey) { + signForAuthorizedService( + authorizedServiceName, + authorizedServiceKeyId, + privateKey + ) { + /* first let's make sure the authorized service is one of the + * listed service names in the PrincipalToken */ + if ( + !this._authorizedServices || + this._authorizedServices.indexOf(authorizedServiceName) === -1 + ) { + throw new Error('Authorized Service is not valid for this token'); + } - /* first let's make sure the authorized service is one of the - * listed service names in the PrincipalToken */ - if (!this._authorizedServices || this._authorizedServices.indexOf(authorizedServiceName) === -1) { - throw new Error('Authorized Service is not valid for this token'); - } + this._authorizedServiceKeyId = authorizedServiceKeyId; + var tokenToSign = this._signedToken + ';bk=' + authorizedServiceKeyId; + + if (this._authorizedServices.length > 1) { + /* if the user has allowed multiple authorized services then we need + * to keep track of which one is re-signing this._token and as such + * we'll store the service name as the value for the bn field */ + this._authorizedServiceName = authorizedServiceName; + tokenToSign += ';bn=' + authorizedServiceName; + } - this._authorizedServiceKeyId = authorizedServiceKeyId; - var tokenToSign = this._signedToken + ';bk=' + authorizedServiceKeyId; + this._authorizedServiceSignature = Crypto.sign( + tokenToSign, + privateKey, + this._digestAlgorithm + ); - if (this._authorizedServices.length > 1) { - /* if the user has allowed multiple authorized services then we need - * to keep track of which one is re-signing this._token and as such - * we'll store the service name as the value for the bn field */ - this._authorizedServiceName = authorizedServiceName; - tokenToSign += ';bn=' + authorizedServiceName; + /* now append our new signature to the token we just signed */ + tokenToSign += ';bs=' + this._authorizedServiceSignature; + this._signedToken = tokenToSign; } - this._authorizedServiceSignature = Crypto.sign(tokenToSign, privateKey, this._digestAlgorithm); + validateForAuthorizedService(publicKey) { + var err = null; + if (!this._authorizedServiceSignature) { + err = new Error( + 'PrincipalToken:validateForAuthorizedService: token=' + + this._unsignedToken + + ' : missing data/signature component: public key=' + + publicKey + ); + winston.error(err); + return false; + } - /* now append our new signature to the token we just signed */ - tokenToSign += ';bs=' + this._authorizedServiceSignature; - this._signedToken = tokenToSign; - } + var idx = this._signedToken.indexOf(';bs='); + if (idx === -1) { + err = new Error( + 'PrincipalToken:validateForAuthorizedService: token=' + + this._unsignedToken + + ' : not signed by any authorized service' + ); + winston.error(err); + return false; + } - validateForAuthorizedService(publicKey) { - var err = null; - if (!this._authorizedServiceSignature) { - err = new Error('PrincipalToken:validateForAuthorizedService: token=' + this._unsignedToken + - ' : missing data/signature component: public key=' + publicKey); - winston.error(err); - return false; - } + var unsignedAuthorizedServiceToken = this._signedToken.substring( + 0, + idx + ); + if (!publicKey) { + err = new Error( + 'PrincipalToken:validateForAuthorizedService: token=' + + this._unsignedToken + + ' : No public key provided' + ); + winston.error(err); + return false; + } - var idx = this._signedToken.indexOf(';bs='); - if (idx === -1) { - err = new Error('PrincipalToken:validateForAuthorizedService: token=' + this._unsignedToken + - ' : not signed by any authorized service'); - winston.error(err); - return false; + var verified = false; // fail safe + try { + verified = Crypto.verify( + unsignedAuthorizedServiceToken, + publicKey, + this._authorizedServiceSignature, + this._digestAlgorithm + ); + if (verified === false) { + err = new Error( + 'PrincipalToken:validateForAuthorizedService: token=' + + this._unsignedToken + + ' : authentication failed: public key=' + + publicKey + ); + winston.error(err); + } + winston.debug( + 'PrincipalToken:validateForAuthorizedService: token=' + + this._unsignedToken + + ' - successfully authenticated' + ); + } catch (e) { + winston.error( + 'PrincipalToken:validateForAuthorizedService: token=' + + this._unsignedToken + + ' : authentication failed verifying signature: exc=' + + e.message + + ' : public key=' + + publicKey + ); + } + return verified; } - var unsignedAuthorizedServiceToken = this._signedToken.substring(0, idx); - if (!publicKey) { - err = new Error('PrincipalToken:validateForAuthorizedService: token=' + this._unsignedToken + - ' : No public key provided'); - winston.error(err); - return false; - } + isValidAuthorizedServiceToken() { + var err = null; + + /* we start our by checking if this is an authorized service token */ + if (!this._authorizedServices) { + /* if both the service name list and signature are not present + * then we have a standard principal token */ + if (!this._authorizedServiceSignature) { + return true; + } + + /* otherwise we have an invalid token without the signature */ + err = new Error( + 'PrincipalToken:isValidAuthorizedServiceToken: Invalid Token=' + + this._unsignedToken + + ' : Authorized Service Signature available without service name' + ); + winston.error(err); + return false; + } - var verified = false; // fail safe - try { - verified = Crypto.verify(unsignedAuthorizedServiceToken, publicKey, this._authorizedServiceSignature, this._digestAlgorithm); - if (verified === false) { - err = new Error('PrincipalToken:validateForAuthorizedService: token=' + this._unsignedToken + - ' : authentication failed: public key=' + publicKey); - winston.error(err); - } - winston.debug('PrincipalToken:validateForAuthorizedService: token=' + this._unsignedToken + - ' - successfully authenticated'); - } catch (e) { - winston.error('PrincipalToken:validateForAuthorizedService: token=' + this._unsignedToken + - ' : authentication failed verifying signature: exc=' + e.message + - ' : public key=' + publicKey); - } - return verified; - } + /* if we have an authorized service name then we must have a corresponding + * signature available in the token */ + if (!this._authorizedServiceSignature) { + err = new Error( + 'PrincipalToken:isValidAuthorizedServiceToken: Invalid Token=' + + this._unsignedToken + + ' : Missing signature for specified authorized service' + ); + winston.error(err); + return false; + } - isValidAuthorizedServiceToken() { - var err = null; + /* if we have a specific authorized service name specified then + * it must be present in our service list otherwise we must + * have a single entry in our list */ + if (this._authorizedServiceName) { + if ( + this._authorizedServices.indexOf( + this._authorizedServiceName + ) === -1 + ) { + err = new Error( + 'PrincipalToken:isValidAuthorizedServiceToken: Invalid Token=' + + this._unsignedToken + + ' : Authorized service name=' + + this._authorizedServiceName + + ' is not listed in the service list' + ); + winston.error(err); + return false; + } + } else if (this._authorizedServices.length !== 1) { + err = new Error( + 'PrincipalToken:isValidAuthorizedServiceToken: Invalid Token=' + + this._unsignedToken + + ' : No service name and Authorized service list contains multiple entries' + ); + winston.error(err); + return false; + } - /* we start our by checking if this is an authorized service token */ - if (!this._authorizedServices) { - /* if both the service name list and signature are not present - * then we have a standard principal token */ - if (!this._authorizedServiceSignature) { return true; - } - - /* otherwise we have an invalid token without the signature */ - err = new Error('PrincipalToken:isValidAuthorizedServiceToken: Invalid Token=' + this._unsignedToken + - ' : Authorized Service Signature available without service name'); - winston.error(err); - return false; } - /* if we have an authorized service name then we must have a corresponding - * signature available in the token */ - if (!this._authorizedServiceSignature) { - err = new Error('PrincipalToken:isValidAuthorizedServiceToken: Invalid Token=' + this._unsignedToken + - ' : Missing signature for specified authorized service'); - winston.error(err); - return false; + getName() { + return this._name; } - /* if we have a specific authorized service name specified then - * it must be present in our service list otherwise we must - * have a single entry in our list */ - if (this._authorizedServiceName) { - if (this._authorizedServices.indexOf(this._authorizedServiceName) === -1) { - err = new Error('PrincipalToken:isValidAuthorizedServiceToken: Invalid Token=' + this._unsignedToken + - ' : Authorized service name=' + this._authorizedServiceName + - ' is not listed in the service list'); - winston.error(err); - return false; - } - } else if (this._authorizedServices.length !== 1) { - err = new Error('PrincipalToken:isValidAuthorizedServiceToken: Invalid Token=' + this._unsignedToken + - ' : No service name and Authorized service list contains multiple entries'); - winston.error(err); - return false; + getKeyService() { + return this._keyService; } - return true; - } - - getName() { - return this._name; - } - - getKeyService() { - return this._keyService; - } - - getOriginalRequestor() { - return this._originalRequestor; - } + getOriginalRequestor() { + return this._originalRequestor; + } - getAuthorizedServices() { - return this._authorizedServices; - } + getAuthorizedServices() { + return this._authorizedServices; + } - getAuthorizedServiceName() { - return this._authorizedServiceName; - } + getAuthorizedServiceName() { + return this._authorizedServiceName; + } - getAuthorizedServiceKeyId() { - return this._authorizedServiceKeyId; - } + getAuthorizedServiceKeyId() { + return this._authorizedServiceKeyId; + } - getAuthorizedServiceSignature() { - return this._authorizedServiceSignature; - } + getAuthorizedServiceSignature() { + return this._authorizedServiceSignature; + } } module.exports = PrincipalToken; diff --git a/libs/nodejs/auth_core/src/token/RoleToken.js b/libs/nodejs/auth_core/src/token/RoleToken.js index 9a5c7f786b4..020de540589 100644 --- a/libs/nodejs/auth_core/src/token/RoleToken.js +++ b/libs/nodejs/auth_core/src/token/RoleToken.js @@ -19,223 +19,254 @@ var Crypto = require('../util/Crypto'); var config = require('../../config/config')(); class RoleToken extends Token { - constructor(token) { - super(); - - winston.level = config.logLevel; - - this._roles = null; - this._principal = null; - this._proxyUser = null; - this._domainCompleteRoleSet = null; - - if (typeof token === 'string') { - try { - this.parseSignedToken(token); - } catch (e) { - throw e; - } - } else { - try { - this.builder(token); - } catch (e) { - throw e; - } - } - } + constructor(token) { + super(); - static setConfig(c) { - super.setConfig(c); - config = Object.assign({}, config, c.auth_core); - } + winston.level = config.logLevel; - /*eslint complexity: ["error", 24]*/ - parseSignedToken(signedToken) { - winston.debug('Constructing RoleToken with input string: ' + signedToken); + this._roles = null; + this._principal = null; + this._proxyUser = null; + this._domainCompleteRoleSet = null; - if (!signedToken) { - throw new Error('Input String signedToken must not be empty'); + if (typeof token === 'string') { + try { + this.parseSignedToken(token); + } catch (e) { + throw e; + } + } else { + try { + this.builder(token); + } catch (e) { + throw e; + } + } } - /** - * first we need to extract data and signature parts - * the signature is always at the end of the token. - * The format for the Token is as follows: - * - * v=Z1;d=sports;r=role1,role2;a=salt;t=tstamp;e=expiry;k=1;s=sig - * - * v: version number Z1 (string) - * d: domain name where the roles are valid for - * r: list of comma separated roles - * c: the list of roles is complete in domain - * p: principal that got the token issued for - * a: random 8 byte salt value hex encoded - * t: timestamp when the token was generated - * h: host that issued this role token - * e: expiry timestamp based on SIA configuration - * k: identifier - either version or zone name - * s: signature generated over the "v=Z1;a=salt;...;e=expiry" string - * using Service's private Key and y64 encoded - * proxy: request was done by this authorized proxy user - */ - var idx = signedToken.indexOf(';s='); - if (idx !== -1) { - this._unsignedToken = signedToken.substring(0, idx); + static setConfig(c) { + super.setConfig(c); + config = Object.assign({}, config, c.auth_core); } - var roleNames = null; - var item = signedToken.split(';'); - for (var i = 0; i < item.length; i++) { - var kv = item[i].split('='); - if (kv.length === 2) { - switch (kv[0]) { - case 'a': - this._salt = kv[1]; - break; - case 'c': - if (Number(kv[1]) === 1) { - this._domainCompleteRoleSet = true; + /*eslint complexity: ["error", 24]*/ + parseSignedToken(signedToken) { + winston.debug( + 'Constructing RoleToken with input string: ' + signedToken + ); + + if (!signedToken) { + throw new Error('Input String signedToken must not be empty'); + } + + /** + * first we need to extract data and signature parts + * the signature is always at the end of the token. + * The format for the Token is as follows: + * + * v=Z1;d=sports;r=role1,role2;a=salt;t=tstamp;e=expiry;k=1;s=sig + * + * v: version number Z1 (string) + * d: domain name where the roles are valid for + * r: list of comma separated roles + * c: the list of roles is complete in domain + * p: principal that got the token issued for + * a: random 8 byte salt value hex encoded + * t: timestamp when the token was generated + * h: host that issued this role token + * e: expiry timestamp based on SIA configuration + * k: identifier - either version or zone name + * s: signature generated over the "v=Z1;a=salt;...;e=expiry" string + * using Service's private Key and y64 encoded + * proxy: request was done by this authorized proxy user + */ + var idx = signedToken.indexOf(';s='); + if (idx !== -1) { + this._unsignedToken = signedToken.substring(0, idx); + } + + var roleNames = null; + var item = signedToken.split(';'); + for (var i = 0; i < item.length; i++) { + var kv = item[i].split('='); + if (kv.length === 2) { + switch (kv[0]) { + case 'a': + this._salt = kv[1]; + break; + case 'c': + if (Number(kv[1]) === 1) { + this._domainCompleteRoleSet = true; + } + break; + case 'd': + this._domain = kv[1]; + break; + case 'e': + this._expiryTime = Number(kv[1]); + break; + case 'h': + this._host = kv[1]; + break; + case 'i': + this._ip = kv[1]; + break; + case 'k': + this._keyId = kv[1]; + break; + case 'p': + this._principal = kv[1]; + break; + case 'r': + roleNames = kv[1]; + break; + case 's': + this._signature = kv[1]; + break; + case 't': + this._timestamp = Number(kv[1]); + break; + case 'proxy': + this._proxyUser = kv[1]; + break; + case 'v': + this._version = kv[1]; + break; + } } - break; - case 'd': - this._domain = kv[1]; - break; - case 'e': - this._expiryTime = Number(kv[1]); - break; - case 'h': - this._host = kv[1]; - break; - case 'i': - this._ip = kv[1]; - break; - case 'k': - this._keyId = kv[1]; - break; - case 'p': - this._principal = kv[1]; - break; - case 'r': - roleNames = kv[1]; - break; - case 's': - this._signature = kv[1]; - break; - case 't': - this._timestamp = Number(kv[1]); - break; - case 'proxy': - this._proxyUser = kv[1]; - break; - case 'v': - this._version = kv[1]; - break; } - } - } - /* the required attributes for the token are - * domain and roles. The signature will be verified - * during the authenticate phase but now we'll make - * sure that domain and roles are present - */ + /* the required attributes for the token are + * domain and roles. The signature will be verified + * during the authenticate phase but now we'll make + * sure that domain and roles are present + */ - if (!this._domain) { - throw new Error('SignedToken does not contain required domain component'); - } + if (!this._domain) { + throw new Error( + 'SignedToken does not contain required domain component' + ); + } - if (!roleNames) { - throw new Error('SignedToken does not contain required roles component'); - } + if (!roleNames) { + throw new Error( + 'SignedToken does not contain required roles component' + ); + } - this._roles = roleNames.split(','); - - this._signedToken = signedToken; - - winston.debug( - 'Values extracted from token ' + - ' version:' + this._version + - ' domain:' + this._domain + - ' roles:' + roleNames + - ' principal:' + this._principal + - ' host:' + this._host + - ' salt:' + this._salt + - ' timestamp:' + this._timestamp + - ' expiryTime:' + this._expiryTime + - ' domainCompleteRoleSet: ' + this.domainCompleteRoleSet + - ' keyId: ' + this._keyId + - ' ip: ' + this._ip + - ' proxyUser: ' + this._proxyUser + - ' signature:' + this._signature); - } - - builder(options) { - if (!options.version || !options.domain || !options.roles) { - throw new Error('version, domain and roles parameters must not be null.'); - } + this._roles = roleNames.split(','); - if (options.version.length === 0 || options.domain.length === 0 || options.roles.length === 0) { - throw new Error('version, domain and roles parameters must have values.'); - } + this._signedToken = signedToken; - // required attributes - this._version = options.version; - this._domain = options.domain; - this._roles = options.roles; - this._principal = options.principal; - this._proxyUser = options.proxyUser; - this._domainCompleteRoleSet = (options.domainCompleteRoleSet || false); - - // optional attributes with default values - this._salt = (options.salt || Crypto.randomSalt()); - this._host = options.host; - this._ip = options.ip; - this._keyId = (options.keyId || '0'); - - this.setTimeStamp((options.issueTime || 0), (options.expirationWindow || 3600)); - - var parts = []; - parts.push('v=' + this._version); - parts.push('d=' + this._domain); - parts.push('r=' + this._roles.join(',')); - if (this._domainCompleteRoleSet) { - parts.push("c=1"); - } - if (this._principal) { - parts.push("p=" + this._principal); - } - if (this._host) { - parts.push('h=' + this._host); + winston.debug( + 'Values extracted from token ' + + ' version:' + + this._version + + ' domain:' + + this._domain + + ' roles:' + + roleNames + + ' principal:' + + this._principal + + ' host:' + + this._host + + ' salt:' + + this._salt + + ' timestamp:' + + this._timestamp + + ' expiryTime:' + + this._expiryTime + + ' domainCompleteRoleSet: ' + + this.domainCompleteRoleSet + + ' keyId: ' + + this._keyId + + ' ip: ' + + this._ip + + ' proxyUser: ' + + this._proxyUser + + ' signature:' + + this._signature + ); } - if (this._proxyUser) { - parts.push('proxy=' + this._proxyUser); - } - parts.push('a=' + this._salt); - parts.push('t=' + this._timestamp); - parts.push('e=' + this._expiryTime); - parts.push('k=' + this._keyId); - if (this._ip) { - parts.push('i=' + this._ip); + + builder(options) { + if (!options.version || !options.domain || !options.roles) { + throw new Error( + 'version, domain and roles parameters must not be null.' + ); + } + + if ( + options.version.length === 0 || + options.domain.length === 0 || + options.roles.length === 0 + ) { + throw new Error( + 'version, domain and roles parameters must have values.' + ); + } + + // required attributes + this._version = options.version; + this._domain = options.domain; + this._roles = options.roles; + this._principal = options.principal; + this._proxyUser = options.proxyUser; + this._domainCompleteRoleSet = options.domainCompleteRoleSet || false; + + // optional attributes with default values + this._salt = options.salt || Crypto.randomSalt(); + this._host = options.host; + this._ip = options.ip; + this._keyId = options.keyId || '0'; + + this.setTimeStamp( + options.issueTime || 0, + options.expirationWindow || 3600 + ); + + var parts = []; + parts.push('v=' + this._version); + parts.push('d=' + this._domain); + parts.push('r=' + this._roles.join(',')); + if (this._domainCompleteRoleSet) { + parts.push('c=1'); + } + if (this._principal) { + parts.push('p=' + this._principal); + } + if (this._host) { + parts.push('h=' + this._host); + } + if (this._proxyUser) { + parts.push('proxy=' + this._proxyUser); + } + parts.push('a=' + this._salt); + parts.push('t=' + this._timestamp); + parts.push('e=' + this._expiryTime); + parts.push('k=' + this._keyId); + if (this._ip) { + parts.push('i=' + this._ip); + } + this._unsignedToken = parts.join(';'); + winston.debug('RoleToken created: ' + this._unsignedToken); } - this._unsignedToken = parts.join(';'); - winston.debug('RoleToken created: ' + this._unsignedToken); - } - getPrincipal() { - return this._principal; - } + getPrincipal() { + return this._principal; + } - getRoles() { - return this._roles; - } + getRoles() { + return this._roles; + } - getProxyUser() { - return this._proxyUser; - } + getProxyUser() { + return this._proxyUser; + } - getDomainCompleteRoleSet() { - return this._domainCompleteRoleSet; - } + getDomainCompleteRoleSet() { + return this._domainCompleteRoleSet; + } } module.exports = RoleToken; diff --git a/libs/nodejs/auth_core/src/token/Token.js b/libs/nodejs/auth_core/src/token/Token.js index 70da09e8d10..1aa092d62a8 100644 --- a/libs/nodejs/auth_core/src/token/Token.js +++ b/libs/nodejs/auth_core/src/token/Token.js @@ -21,174 +21,225 @@ var ATHENZ_TOKEN_MAX_EXPIRY; var ATHENZ_TOKEN_NO_EXPIRY; class Token { - constructor() { - winston.level = config.logLevel; - - ATHENZ_TOKEN_MAX_EXPIRY = (Number(config.tokenMaxExpiry) > 30 * 24 * 60 * 60) ? 30 * 24 * 60 * 60 : Number(config.tokenMaxExpiry); - ATHENZ_TOKEN_NO_EXPIRY = config.tokenNoExpiry; - - this._unsignedToken = null; - this._signedToken = null; - this._version = null; - this._salt = null; - this._host = null; - this._ip = null; - this._domain = null; - this._signature = null; - this._keyId = '0'; - this._expiryTime = 0; - this._timestamp = 0; - this._digestAlgorithm = 'SHA256'; - } - - static setConfig(c) { - config = Object.assign({}, config, c.auth_core); - } - - sign(privateKey) { - try { - this._signature = Crypto.sign(this._unsignedToken, privateKey, this._digestAlgorithm); - this._signedToken = this._unsignedToken + ';s=' + this._signature; - } catch (e) { - throw e; - } - } - - setTimeStamp(issueTime, expirationWindow) { - this._timestamp = (issueTime > 0) ? issueTime : Math.floor(Date.now() / 1000); - this._expiryTime = this._timestamp + expirationWindow; - } - - /*eslint complexity: ["error", 12]*/ - validate(publicKey, allowedOffset, allowNoExpiry) { - var err = null; - if (!this._unsignedToken || !this._signature) { - err = new Error('Token:validate: token=' + this._unsignedToken + - ' : missing data/signature component'); - winston.error(err); - return false; - } - - if (!publicKey) { - err = new Error('Token:validate: token=' + this._unsignedToken + - ' : No public key provided'); - winston.error(err); - return false; - } - - var now = Math.floor(Date.now() / 1000); - - /* make sure the token does not have a timestamp in the future - * we'll allow the configured offset between servers */ - if (this._timestamp !== 0 && this._timestamp - allowedOffset > now) { - err = new Error('Token:validate: token=' + this._unsignedToken + - ' : has future timestamp=' + this._timestamp + - ' : current time=' + now + - ' : allowed offset=' + allowedOffset); - winston.error(err); - return false; - } - - /* make sure we don't have unlimited tokens unless we have - * explicitly enabled that option for our system. by default - * they should have an expiration date of less than 30 days */ - if (this._expiryTime !== 0 || !ATHENZ_TOKEN_NO_EXPIRY || !allowNoExpiry) { - if (this._expiryTime < now) { - err = new Error('Token:validate: token=' + this._unsignedToken + - ' : has expired time=' + this._expiryTime + - ' : current time=' + now); - winston.error(err); - return false; - } - if (this._expiryTime > now + ATHENZ_TOKEN_MAX_EXPIRY + allowedOffset) { - err = new Error('Token:validate: token=' + this._unsignedToken + - ' : expires too far in the future=' + this._expiryTime + - ' : current time=' + now + - ' : max expiry=' + ATHENZ_TOKEN_MAX_EXPIRY + - ' : allowed offset=' + allowedOffset); - winston.error(err); - return false; - } - } - - var verified = false; //fail safe - try { - verified = Crypto.verify(this._unsignedToken, publicKey, this._signature, this._digestAlgorithm); - if (verified === false) { - err = new Error('Token:validate: token=' + this._unsignedToken + - ' : authentication failed'); - winston.error(err); - } else { - winston.debug('validate: Token successfully authenticated'); - } - } catch (e) { - winston.error('Token:validate: token=' + this._unsignedToken + - ' : verify signature failed due to Exception=' + e.message); - } - - return verified; - } - - getVersion() { - return this._version; - } - - getSalt() { - return this._salt; - } - - getHost() { - return this._host; - } - - getDomain() { - return this._domain; - } - - getSignature() { - return this._signature; - } - - getTimestamp() { - return this._timestamp; - } - - getExpiryTime() { - return this._expiryTime; - } - - getSignedToken() { - return this._signedToken; - } - - getKeyId() { - return this._keyId; - } - - getIP() { - return this._ip; - } - - getUnsignedToken() { - return this._unsignedToken; - } - - /** - * Helper method to parse a credential to remove the signature from the - * raw credential string. Returning the unsigned credential. - * @param credential full token credentials including signature - * @return credentials without the signature - **/ - static getUnsignedToken(credential) { - if (credential) { - var idx = credential.indexOf(';s='); - if (idx !== -1) { - credential = credential.substring(0, idx); - } - } - - return credential; - } + constructor() { + winston.level = config.logLevel; + + ATHENZ_TOKEN_MAX_EXPIRY = + Number(config.tokenMaxExpiry) > 30 * 24 * 60 * 60 + ? 30 * 24 * 60 * 60 + : Number(config.tokenMaxExpiry); + ATHENZ_TOKEN_NO_EXPIRY = config.tokenNoExpiry; + + this._unsignedToken = null; + this._signedToken = null; + this._version = null; + this._salt = null; + this._host = null; + this._ip = null; + this._domain = null; + this._signature = null; + this._keyId = '0'; + this._expiryTime = 0; + this._timestamp = 0; + this._digestAlgorithm = 'SHA256'; + } + + static setConfig(c) { + config = Object.assign({}, config, c.auth_core); + } + + sign(privateKey) { + try { + this._signature = Crypto.sign( + this._unsignedToken, + privateKey, + this._digestAlgorithm + ); + this._signedToken = this._unsignedToken + ';s=' + this._signature; + } catch (e) { + throw e; + } + } + + setTimeStamp(issueTime, expirationWindow) { + this._timestamp = + issueTime > 0 ? issueTime : Math.floor(Date.now() / 1000); + this._expiryTime = this._timestamp + expirationWindow; + } + + /*eslint complexity: ["error", 12]*/ + validate(publicKey, allowedOffset, allowNoExpiry) { + var err = null; + if (!this._unsignedToken || !this._signature) { + err = new Error( + 'Token:validate: token=' + + this._unsignedToken + + ' : missing data/signature component' + ); + winston.error(err); + return false; + } + + if (!publicKey) { + err = new Error( + 'Token:validate: token=' + + this._unsignedToken + + ' : No public key provided' + ); + winston.error(err); + return false; + } + + var now = Math.floor(Date.now() / 1000); + + /* make sure the token does not have a timestamp in the future + * we'll allow the configured offset between servers */ + if (this._timestamp !== 0 && this._timestamp - allowedOffset > now) { + err = new Error( + 'Token:validate: token=' + + this._unsignedToken + + ' : has future timestamp=' + + this._timestamp + + ' : current time=' + + now + + ' : allowed offset=' + + allowedOffset + ); + winston.error(err); + return false; + } + + /* make sure we don't have unlimited tokens unless we have + * explicitly enabled that option for our system. by default + * they should have an expiration date of less than 30 days */ + if ( + this._expiryTime !== 0 || + !ATHENZ_TOKEN_NO_EXPIRY || + !allowNoExpiry + ) { + if (this._expiryTime < now) { + err = new Error( + 'Token:validate: token=' + + this._unsignedToken + + ' : has expired time=' + + this._expiryTime + + ' : current time=' + + now + ); + winston.error(err); + return false; + } + if ( + this._expiryTime > + now + ATHENZ_TOKEN_MAX_EXPIRY + allowedOffset + ) { + err = new Error( + 'Token:validate: token=' + + this._unsignedToken + + ' : expires too far in the future=' + + this._expiryTime + + ' : current time=' + + now + + ' : max expiry=' + + ATHENZ_TOKEN_MAX_EXPIRY + + ' : allowed offset=' + + allowedOffset + ); + winston.error(err); + return false; + } + } + + var verified = false; //fail safe + try { + verified = Crypto.verify( + this._unsignedToken, + publicKey, + this._signature, + this._digestAlgorithm + ); + if (verified === false) { + err = new Error( + 'Token:validate: token=' + + this._unsignedToken + + ' : authentication failed' + ); + winston.error(err); + } else { + winston.debug('validate: Token successfully authenticated'); + } + } catch (e) { + winston.error( + 'Token:validate: token=' + + this._unsignedToken + + ' : verify signature failed due to Exception=' + + e.message + ); + } + + return verified; + } + + getVersion() { + return this._version; + } + + getSalt() { + return this._salt; + } + + getHost() { + return this._host; + } + + getDomain() { + return this._domain; + } + + getSignature() { + return this._signature; + } + + getTimestamp() { + return this._timestamp; + } + + getExpiryTime() { + return this._expiryTime; + } + + getSignedToken() { + return this._signedToken; + } + + getKeyId() { + return this._keyId; + } + + getIP() { + return this._ip; + } + + getUnsignedToken() { + return this._unsignedToken; + } + + /** + * Helper method to parse a credential to remove the signature from the + * raw credential string. Returning the unsigned credential. + * @param credential full token credentials including signature + * @return credentials without the signature + **/ + static getUnsignedToken(credential) { + if (credential) { + var idx = credential.indexOf(';s='); + if (idx !== -1) { + credential = credential.substring(0, idx); + } + } + + return credential; + } } module.exports = Token; diff --git a/libs/nodejs/auth_core/src/util/Crypto.js b/libs/nodejs/auth_core/src/util/Crypto.js index 2f2cb702d29..e57d78b829b 100644 --- a/libs/nodejs/auth_core/src/util/Crypto.js +++ b/libs/nodejs/auth_core/src/util/Crypto.js @@ -19,41 +19,41 @@ var ybase64 = require('./YBase64'); var SALT_BYTES = 4; class Crypto { - static hmac(message, sharedSecret) { - try { - var hmac = crypto.createHmac('sha256', sharedSecret); - hmac.update(message); - return ybase64.ybase64Encode(hmac.digest()); - } catch (e) { - throw new Error('Crypto:hmac:' + e.message); + static hmac(message, sharedSecret) { + try { + var hmac = crypto.createHmac('sha256', sharedSecret); + hmac.update(message); + return ybase64.ybase64Encode(hmac.digest()); + } catch (e) { + throw new Error('Crypto:hmac:' + e.message); + } } - } - static sign(message, key, digestAlgorithm) { - try { - var sign = crypto.createSign(digestAlgorithm); - sign.update(message); - return ybase64.ybase64Encode(sign.sign(key)); - } catch (e) { - throw new Error('Crypto:sign:' + e.message); + static sign(message, key, digestAlgorithm) { + try { + var sign = crypto.createSign(digestAlgorithm); + sign.update(message); + return ybase64.ybase64Encode(sign.sign(key)); + } catch (e) { + throw new Error('Crypto:sign:' + e.message); + } } - } - static verify(message, key, signature, digestAlgorithm) { - try { - var sig = ybase64.ybase64Decode(signature); - var verify = crypto.createVerify(digestAlgorithm); - verify.update(message); - return verify.verify(key, sig); - } catch (e) { - throw new Error('Crypto:verify:' + e.message); + static verify(message, key, signature, digestAlgorithm) { + try { + var sig = ybase64.ybase64Decode(signature); + var verify = crypto.createVerify(digestAlgorithm); + verify.update(message); + return verify.verify(key, sig); + } catch (e) { + throw new Error('Crypto:verify:' + e.message); + } } - } - static randomSalt() { - var salt = crypto.randomBytes(SALT_BYTES); - return salt.toString('hex'); - } + static randomSalt() { + var salt = crypto.randomBytes(SALT_BYTES); + return salt.toString('hex'); + } } module.exports = Crypto; diff --git a/libs/nodejs/auth_core/src/util/Validate.js b/libs/nodejs/auth_core/src/util/Validate.js index 23a4aebf3b1..29530280fbb 100644 --- a/libs/nodejs/auth_core/src/util/Validate.js +++ b/libs/nodejs/auth_core/src/util/Validate.js @@ -14,20 +14,24 @@ 'use strict'; class Validate { - static principalName(name) { - var reg = new RegExp('((([a-zA-Z_][a-zA-Z0-9_-]*\\.)*[a-zA-Z_][a-zA-Z0-9_-]*):)?(([a-zA-Z_][a-zA-Z0-9_-]*\\.)*[a-zA-Z_][a-zA-Z0-9_-]*)'); - return this._checkReg(reg, name); - } + static principalName(name) { + var reg = new RegExp( + '((([a-zA-Z_][a-zA-Z0-9_-]*\\.)*[a-zA-Z_][a-zA-Z0-9_-]*):)?(([a-zA-Z_][a-zA-Z0-9_-]*\\.)*[a-zA-Z_][a-zA-Z0-9_-]*)' + ); + return this._checkReg(reg, name); + } - static domainName(name) { - var reg = new RegExp('([a-zA-Z_][a-zA-Z0-9_-]*\\.)*[a-zA-Z_][a-zA-Z0-9_-]*'); - return this._checkReg(reg, name); - } + static domainName(name) { + var reg = new RegExp( + '([a-zA-Z_][a-zA-Z0-9_-]*\\.)*[a-zA-Z_][a-zA-Z0-9_-]*' + ); + return this._checkReg(reg, name); + } - static _checkReg(reg, name) { - var result = reg.exec(name); - return (result && result[0] === name) ? true : false; - } + static _checkReg(reg, name) { + var result = reg.exec(name); + return result && result[0] === name ? true : false; + } } module.exports = Validate; diff --git a/libs/nodejs/auth_core/src/util/YBase64.js b/libs/nodejs/auth_core/src/util/YBase64.js index 3e0d73866f0..35106932b2c 100644 --- a/libs/nodejs/auth_core/src/util/YBase64.js +++ b/libs/nodejs/auth_core/src/util/YBase64.js @@ -14,19 +14,25 @@ 'use strict'; class YBase64 { - static ybase64Encode(input) { - var buffer = (Buffer.isBuffer(input)) ? input : Buffer.from(input); - var encoded = buffer.toString('base64'); - return encoded.replace(/\+/g, '.').replace(/\//g, '_').replace(/=/g, '-'); - } + static ybase64Encode(input) { + var buffer = Buffer.isBuffer(input) ? input : Buffer.from(input); + var encoded = buffer.toString('base64'); + return encoded + .replace(/\+/g, '.') + .replace(/\//g, '_') + .replace(/=/g, '-'); + } - static ybase64Decode(input) { - if ('string' !== typeof input) { - throw new Error(input + ' is not string'); + static ybase64Decode(input) { + if ('string' !== typeof input) { + throw new Error(input + ' is not string'); + } + var encoded = input + .replace(/\./g, '+') + .replace(/_/g, '/') + .replace(/-/g, '='); + return Buffer.from(encoded, 'base64'); } - var encoded = input.replace(/\./g, '+').replace(/_/g, '/').replace(/-/g, '='); - return Buffer.from(encoded, 'base64'); - } } module.exports = YBase64; diff --git a/libs/nodejs/auth_core/test/config/KeyStore.js b/libs/nodejs/auth_core/test/config/KeyStore.js index 527a185c4b5..0c0fc08a229 100644 --- a/libs/nodejs/auth_core/test/config/KeyStore.js +++ b/libs/nodejs/auth_core/test/config/KeyStore.js @@ -16,12 +16,12 @@ var fs = require('fs'); class KeyStore { - static getPublicKey(domain, service, keyId) { - if (!domain || !service || !keyId) { - return null; + static getPublicKey(domain, service, keyId) { + if (!domain || !service || !keyId) { + return null; + } + return fs.readFileSync(__dirname + '/../resources/public_k0.pem'); } - return fs.readFileSync(__dirname + '/../resources/public_k0.pem'); - } } module.exports = KeyStore; diff --git a/libs/nodejs/auth_core/test/config/PrincipalAuthorityMock.js b/libs/nodejs/auth_core/test/config/PrincipalAuthorityMock.js index 99d95ca31ab..d193dce1162 100644 --- a/libs/nodejs/auth_core/test/config/PrincipalAuthorityMock.js +++ b/libs/nodejs/auth_core/test/config/PrincipalAuthorityMock.js @@ -14,13 +14,13 @@ 'use strict'; class PrincipalAuthorityMock { - static getPublicKey(domain, service, keyId) { - if (!domain || !service || !keyId) { - return null; - } + static getPublicKey(domain, service, keyId) { + if (!domain || !service || !keyId) { + return null; + } - return domain + '.' + service + '.' + keyId; - } + return domain + '.' + service + '.' + keyId; + } } module.exports = PrincipalAuthorityMock; diff --git a/libs/nodejs/auth_core/test/config/SimplePrincipalMock.js b/libs/nodejs/auth_core/test/config/SimplePrincipalMock.js index 50fe92bfdb2..35a102fd132 100644 --- a/libs/nodejs/auth_core/test/config/SimplePrincipalMock.js +++ b/libs/nodejs/auth_core/test/config/SimplePrincipalMock.js @@ -14,17 +14,17 @@ 'use strict'; class SimplePrincipalMock { - constructor (domain){ - this._domain = domain; - } + constructor(domain) { + this._domain = domain; + } - setDomain(domain) { - this._domain = domain; - } + setDomain(domain) { + this._domain = domain; + } - getDomain(domain) { - return this._domain; - } + getDomain(domain) { + return this._domain; + } } module.exports = SimplePrincipalMock; diff --git a/libs/nodejs/auth_core/test/config/helpers.js b/libs/nodejs/auth_core/test/config/helpers.js index 9b5bb91837e..1d480c86f3d 100644 --- a/libs/nodejs/auth_core/test/config/helpers.js +++ b/libs/nodejs/auth_core/test/config/helpers.js @@ -13,10 +13,12 @@ */ 'use strict'; -var privateKey = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBcGZoVnhPT2VaQVhMNkxhYzY3dXFXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOCk53UWs5cU1XSVNHRDZ3NSt4aW5OOUZ2YWtXRndsblJhY1JmaWtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWoKMzlhdXM0aFZuK2lCZjZWVFZWN0tyTVdQMnRkRDkybW4xdTJTM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UgpBQzVVbnpTKzZlTjF2RTRxOTNNMkdXSk1qcVVaZXQ1RHJwQVcrWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5Cnd3SEJDS2lDNW9xNmJWVXlLOUNBay9qejJYSW5oRjB2T0RtQlJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlEKZFJlYVF0dWt3R3JLK0EvdGZBVFYvSWs5Tk14Um95V2FUd2o4MFFJREFRQUJBb0lCQUZTQ0lGb0NkSElGcVpBeQorNi9rZkpjalMwRTlhMlU3VC9SZ2dZREpteDVnTmV6L1JtM1BCR3M1RzFsL1VVWnN1UXg1Mk02bHZxTHI5SUlICmVNM0JGYk1SaHl6QWYycEZ2ajlnbUtTeEJWWkFISlhHY2hwVm05cjZmbTdQMnJCK0kvS0NNN3pKVTdoZ2g5RUEKMFY5VTFNV2I4U0JHUmhPdmh1UW9zZCtMNkpxQmYzOUkrN1BWdFRxNjZuNXVtWGU1WlU1eTJiMmRsSmRTU1hXeQpjc1BSMWUzT1IvTnM5dDVJTVdHcnR2VUowWHlWMjM3eENVM2tmOEg4aDA0NWd6eUdrZHE5djZIVHJhQzZMaTF5CmdwRDhVMFQ2VVQwWHBGcWowbGttTk1NcHNqOWw1QnVSR2ZPNjVhb0ZBOWl5S2xMdndaTnJ2YnNZL0UrNUJjRnMKQWNJczY2a0NnWUVBenAxU1VZZ2FGaWZFQnE3cGluNTY3S3FQemFCcURLYUZTekhkWnFsR1RDVzJ4bDJIY2J4YwpObWJseXUrU21rdzFQZGFlbzF3SXh3U052eU8xWTE1Y0pGbzBteUljUzZRZFJFZmhwWjU0dUdqOE9YQ0RzeW10Ci94TVEwVVlzdFJPaXhDVTlVL1lFdnh3TzlpUkRpU0VsdXd3cDBuN09LdjRRTTVxVkliMFdydjhDZ1lFQXphUDkKbW9ROFFDNS9mRlg0eE5tOFFGOXp5Ukc3SnRndysrNWJYbkFXVFgzcWpIaUxlQ1ZZdHpidlFVY1g0cFp5YlRqaAowYm5yUXpiZmJFVFFZMnk4ZGdqWTVjMEpPNmNTVm11QkE4Lzc4aHl4WUZTTzV0N01CSS93ak42aU80VWhHSGdKCnc3Q1VVM013RDMwWlNZQ3JwNDZyZE5yamdUc3dmN2crc3N1OUpDOENnWUJvMjFhbm9oYjdIM3RRbVB4VkdSTngKZ0s0eWdUTFE4TUc5QTdXRklHdFl3ZHNjbU9MZ1NlUFNpQzRlNjY3UE45WGRhRXBpUlpiK3ljVFdPRjBaN1ExKwpOWGwxTWI2Q2RPdVZkNVdBNUFnSUx0K3lsdk4vdmF0Y1JHVElrSUNuOVNzcHVHeURhOXZFMFl5V1Jwa3Z3dTdQCkdzRXUzc1BxOWIxck13eDBidTVRS1FLQmdBeU1TQ3BJaldDaE5iaEJpcmVBVGNOano2M2lQaGhGc3Q5OGtPaTMKVURVVVRONmJjRzg1WUN0MTE2MlZCL2tVa3hEbEdxcHdmTkdTSkpuM3JQdVVJLy9UMUdCWlhZbmRUUG9tL3kxYgpZSlZLZU94VzNJMXI1T2tXVzJoTklYc2VTWUd6dVd6T2RvNk5CYzY4SkhIZXZ4cXZVdmtEYmtSeGR5a2o5ZmQxCkJTcVRBb0dBVG1nZ05MQk5tNzh0ZWg1cGVyY2RTSXV2VWFMNzRzaXlKbFJlWW5ETitEeUQyUDMwaE9VVUVSYWEKVm9yaFlLdTdTdWVHclBhRy8vYWxZa3hGTGs1Q0RkVmovMWdaS3dTTWc0L3RaRm1yYXBOMFVmdDdGQkx1N1RYTApab1NSeVFsZFo0ZGRpR0xsQ1hlbGh4STdjSnJoQlN3UytYcVVPdnk0V29oYTdsYndYems9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg--"; -var publicKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwZmhWeE9PZVpBWEw2TGFjNjd1cQpXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOTndRazlxTVdJU0dENnc1K3hpbk45RnZha1dGd2xuUmFjUmZpCmtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWozOWF1czRoVm4raUJmNlZUVlY3S3JNV1AydGREOTJtbjF1MlMKM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UkFDNVVuelMrNmVOMXZFNHE5M00yR1dKTWpxVVpldDVEcnBBVworWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5d3dIQkNLaUM1b3E2YlZVeUs5Q0FrL2p6MlhJbmhGMHZPRG1CClJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlFkUmVhUXR1a3dHcksrQS90ZkFUVi9JazlOTXhSb3lXYVR3ajgKMFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--"; +var privateKey = + 'LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBcGZoVnhPT2VaQVhMNkxhYzY3dXFXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOCk53UWs5cU1XSVNHRDZ3NSt4aW5OOUZ2YWtXRndsblJhY1JmaWtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWoKMzlhdXM0aFZuK2lCZjZWVFZWN0tyTVdQMnRkRDkybW4xdTJTM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UgpBQzVVbnpTKzZlTjF2RTRxOTNNMkdXSk1qcVVaZXQ1RHJwQVcrWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5Cnd3SEJDS2lDNW9xNmJWVXlLOUNBay9qejJYSW5oRjB2T0RtQlJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlEKZFJlYVF0dWt3R3JLK0EvdGZBVFYvSWs5Tk14Um95V2FUd2o4MFFJREFRQUJBb0lCQUZTQ0lGb0NkSElGcVpBeQorNi9rZkpjalMwRTlhMlU3VC9SZ2dZREpteDVnTmV6L1JtM1BCR3M1RzFsL1VVWnN1UXg1Mk02bHZxTHI5SUlICmVNM0JGYk1SaHl6QWYycEZ2ajlnbUtTeEJWWkFISlhHY2hwVm05cjZmbTdQMnJCK0kvS0NNN3pKVTdoZ2g5RUEKMFY5VTFNV2I4U0JHUmhPdmh1UW9zZCtMNkpxQmYzOUkrN1BWdFRxNjZuNXVtWGU1WlU1eTJiMmRsSmRTU1hXeQpjc1BSMWUzT1IvTnM5dDVJTVdHcnR2VUowWHlWMjM3eENVM2tmOEg4aDA0NWd6eUdrZHE5djZIVHJhQzZMaTF5CmdwRDhVMFQ2VVQwWHBGcWowbGttTk1NcHNqOWw1QnVSR2ZPNjVhb0ZBOWl5S2xMdndaTnJ2YnNZL0UrNUJjRnMKQWNJczY2a0NnWUVBenAxU1VZZ2FGaWZFQnE3cGluNTY3S3FQemFCcURLYUZTekhkWnFsR1RDVzJ4bDJIY2J4YwpObWJseXUrU21rdzFQZGFlbzF3SXh3U052eU8xWTE1Y0pGbzBteUljUzZRZFJFZmhwWjU0dUdqOE9YQ0RzeW10Ci94TVEwVVlzdFJPaXhDVTlVL1lFdnh3TzlpUkRpU0VsdXd3cDBuN09LdjRRTTVxVkliMFdydjhDZ1lFQXphUDkKbW9ROFFDNS9mRlg0eE5tOFFGOXp5Ukc3SnRndysrNWJYbkFXVFgzcWpIaUxlQ1ZZdHpidlFVY1g0cFp5YlRqaAowYm5yUXpiZmJFVFFZMnk4ZGdqWTVjMEpPNmNTVm11QkE4Lzc4aHl4WUZTTzV0N01CSS93ak42aU80VWhHSGdKCnc3Q1VVM013RDMwWlNZQ3JwNDZyZE5yamdUc3dmN2crc3N1OUpDOENnWUJvMjFhbm9oYjdIM3RRbVB4VkdSTngKZ0s0eWdUTFE4TUc5QTdXRklHdFl3ZHNjbU9MZ1NlUFNpQzRlNjY3UE45WGRhRXBpUlpiK3ljVFdPRjBaN1ExKwpOWGwxTWI2Q2RPdVZkNVdBNUFnSUx0K3lsdk4vdmF0Y1JHVElrSUNuOVNzcHVHeURhOXZFMFl5V1Jwa3Z3dTdQCkdzRXUzc1BxOWIxck13eDBidTVRS1FLQmdBeU1TQ3BJaldDaE5iaEJpcmVBVGNOano2M2lQaGhGc3Q5OGtPaTMKVURVVVRONmJjRzg1WUN0MTE2MlZCL2tVa3hEbEdxcHdmTkdTSkpuM3JQdVVJLy9UMUdCWlhZbmRUUG9tL3kxYgpZSlZLZU94VzNJMXI1T2tXVzJoTklYc2VTWUd6dVd6T2RvNk5CYzY4SkhIZXZ4cXZVdmtEYmtSeGR5a2o5ZmQxCkJTcVRBb0dBVG1nZ05MQk5tNzh0ZWg1cGVyY2RTSXV2VWFMNzRzaXlKbFJlWW5ETitEeUQyUDMwaE9VVUVSYWEKVm9yaFlLdTdTdWVHclBhRy8vYWxZa3hGTGs1Q0RkVmovMWdaS3dTTWc0L3RaRm1yYXBOMFVmdDdGQkx1N1RYTApab1NSeVFsZFo0ZGRpR0xsQ1hlbGh4STdjSnJoQlN3UytYcVVPdnk0V29oYTdsYndYems9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg--'; +var publicKey = + 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwZmhWeE9PZVpBWEw2TGFjNjd1cQpXaEF3cEV5ZzRLeTk4Z3FTQzdjZWxDWG1Xd1pOTndRazlxTVdJU0dENnc1K3hpbk45RnZha1dGd2xuUmFjUmZpCmtyUjdwd0hqYzgydHRVVW0wQWpxSVdYU3lHOWozOWF1czRoVm4raUJmNlZUVlY3S3JNV1AydGREOTJtbjF1MlMKM2lTcERWdjRLTXRZeWNvaGEwN2hVU1E1ZDM0UkFDNVVuelMrNmVOMXZFNHE5M00yR1dKTWpxVVpldDVEcnBBVworWElrUUpQWUY5eGJnTUFFWVFUWTltbHY1MUo5d3dIQkNLaUM1b3E2YlZVeUs5Q0FrL2p6MlhJbmhGMHZPRG1CClJrYjA4eEFRMmpmc2NnVGRoSDNZenRYWEVrTlFkUmVhUXR1a3dHcksrQS90ZkFUVi9JazlOTXhSb3lXYVR3ajgKMFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg--'; module.exports = { - privateKey: privateKey, - publicKey: publicKey + privateKey: privateKey, + publicKey: publicKey, }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000000..48e341a0954 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +}