diff --git a/.jshintrc b/.jshintrc index 837ad10f..3f9f6050 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,5 +1,5 @@ { - "asi" : false, + "asi" : true, "esversion": 6, "node": true } \ No newline at end of file diff --git a/example/index.js b/example/index.js index b7e14f46..61b9835b 100644 --- a/example/index.js +++ b/example/index.js @@ -14,41 +14,41 @@ * the License. */ -const Keycloak = require('keycloak-connect'); -const hogan = require('hogan-express'); -const express = require('express'); -const session = require('express-session'); +const Keycloak = require('keycloak-connect') +const hogan = require('hogan-express') +const express = require('express') +const session = require('express-session') -const app = express(); +const app = express() const server = app.listen(3000, function () { - const host = server.address().address; - const port = server.address().port; - console.log('Example app listening at http://%s:%s', host, port); -}); + const host = server.address().address + const port = server.address().port + console.log('Example app listening at http://%s:%s', host, port) +}) // Register '.mustache' extension with The Mustache Express -app.set('view engine', 'html'); -app.set('views', require('path').join(__dirname, '/view')); -app.engine('html', hogan); +app.set('view engine', 'html') +app.set('views', require('path').join(__dirname, '/view')) +app.engine('html', hogan) // A normal un-protected public URL. app.get('/', function (req, res) { - res.render('index'); -}); + res.render('index') +}) // Create a session-store to be used by both the express-session // middleware and the keycloak middleware. -const memoryStore = new session.MemoryStore(); +const memoryStore = new session.MemoryStore() app.use(session({ secret: 'mySecret', resave: false, saveUninitialized: true, store: memoryStore -})); +})) // Provide the session store to the Keycloak so that sessions // can be invalidated from the Keycloak console callback. @@ -58,7 +58,7 @@ app.use(session({ const keycloak = new Keycloak({ store: memoryStore -}); +}) // Install the Keycloak middleware. // @@ -73,14 +73,14 @@ app.use(keycloak.middleware({ logout: '/logout', admin: '/', protected: '/protected/resource' -})); +})) app.get('/login', keycloak.protect(), function (req, res) { res.render('index', { result: JSON.stringify(JSON.parse(req.session['keycloak-token']), null, 4), event: '1. Authentication\n2. Login' - }); -}); + }) +}) app.get('/protected/resource', keycloak.enforcer(['resource:view', 'resource:write'], { resource_server_id: 'nodejs-apiserver' @@ -88,5 +88,5 @@ app.get('/protected/resource', keycloak.enforcer(['resource:view', 'resource:wri res.render('index', { result: JSON.stringify(JSON.parse(req.session['keycloak-token']), null, 4), event: '1. Access granted to Default Resource\n' - }); -}); + }) +}) diff --git a/keycloak.js b/keycloak.js index 98f460b1..101233f9 100644 --- a/keycloak.js +++ b/keycloak.js @@ -14,20 +14,20 @@ * the License. */ -const BearerStore = require('./stores/bearer-store'); -const CookieStore = require('./stores/cookie-store'); -const SessionStore = require('./stores/session-store'); - -const Config = require('./middleware/auth-utils/config'); -const GrantManager = require('./middleware/auth-utils/grant-manager'); -const Setup = require('./middleware/setup'); -const Admin = require('./middleware/admin'); -const Logout = require('./middleware/logout'); -const PostAuth = require('./middleware/post-auth'); -const GrantAttacher = require('./middleware/grant-attacher'); -const Protect = require('./middleware/protect'); -const Enforcer = require('./middleware/enforcer'); -const CheckSso = require('./middleware/check-sso'); +const BearerStore = require('./stores/bearer-store') +const CookieStore = require('./stores/cookie-store') +const SessionStore = require('./stores/session-store') + +const Config = require('./middleware/auth-utils/config') +const GrantManager = require('./middleware/auth-utils/grant-manager') +const Setup = require('./middleware/setup') +const Admin = require('./middleware/admin') +const Logout = require('./middleware/logout') +const PostAuth = require('./middleware/post-auth') +const GrantAttacher = require('./middleware/grant-attacher') +const Protect = require('./middleware/protect') +const Enforcer = require('./middleware/enforcer') +const CheckSso = require('./middleware/check-sso') /** * Instantiate a Keycloak. @@ -60,30 +60,30 @@ const CheckSso = require('./middleware/check-sso'); */ function Keycloak (config, keycloakConfig) { // If keycloakConfig is null, Config() will search for `keycloak.json`. - this.config = new Config(keycloakConfig); + this.config = new Config(keycloakConfig) - this.grantManager = new GrantManager(this.config); + this.grantManager = new GrantManager(this.config) - this.stores = [BearerStore]; + this.stores = [BearerStore] if (!config) { - throw new Error('Adapter configuration must be provided.'); + throw new Error('Adapter configuration must be provided.') } // Add the custom scope value - this.config.scope = config.scope; + this.config.scope = config.scope if (config && config.store && config.cookies) { - throw new Error('Either `store` or `cookies` may be set, but not both'); + throw new Error('Either `store` or `cookies` may be set, but not both') } if (config && config.store) { - this.stores.push(new SessionStore(config.store)); + this.stores.push(new SessionStore(config.store)) } else if (config && config.cookies) { - this.stores.push(CookieStore); + this.stores.push(CookieStore) } - this.config.idpHint = config.idpHint; + this.config.idpHint = config.idpHint } /** @@ -109,22 +109,22 @@ function Keycloak (config, keycloakConfig) { */ Keycloak.prototype.middleware = function (options) { if (!options) { - options = { logout: '', admin: '' }; + options = { logout: '', admin: '' } } - options.logout = options.logout || '/logout'; - options.admin = options.admin || '/'; + options.logout = options.logout || '/logout' + options.admin = options.admin || '/' - const middlewares = []; + const middlewares = [] - middlewares.push(Setup); - middlewares.push(PostAuth(this)); - middlewares.push(Admin(this, options.admin)); - middlewares.push(GrantAttacher(this)); - middlewares.push(Logout(this, options.logout)); + middlewares.push(Setup) + middlewares.push(PostAuth(this)) + middlewares.push(Admin(this, options.admin)) + middlewares.push(GrantAttacher(this)) + middlewares.push(Logout(this, options.logout)) - return middlewares; -}; + return middlewares +} /** * Apply protection middleware to an application or specific URL. @@ -186,8 +186,8 @@ Keycloak.prototype.middleware = function (options) { * @param {String} spec The protection spec (optional) */ Keycloak.prototype.protect = function (spec) { - return Protect(this, spec); -}; + return Protect(this, spec) +} /** * Enforce access based on the given permissions. This method operates in two modes, depending on the `response_mode` @@ -238,8 +238,8 @@ Keycloak.prototype.protect = function (spec) { * @param {String[]} expectedPermissions A single string representing a permission or an arrat of strings representing the permissions. For instance, 'item:read' or ['item:read', 'item:write']. */ Keycloak.prototype.enforcer = function (permissions, config) { - return new Enforcer(this, config).enforce(permissions); -}; + return new Enforcer(this, config).enforce(permissions) +} /** * Apply check SSO middleware to an application or specific URL. @@ -250,8 +250,8 @@ Keycloak.prototype.enforcer = function (permissions, config) { * */ Keycloak.prototype.checkSso = function () { - return CheckSso(this); -}; + return CheckSso(this) +} /** * Callback made upon successful authentication of a user. @@ -272,7 +272,7 @@ Keycloak.prototype.checkSso = function () { */ Keycloak.prototype.authenticated = function (request) { // no-op -}; +} /** * Callback made upon successful de-authentication of a user. @@ -285,7 +285,7 @@ Keycloak.prototype.authenticated = function (request) { */ Keycloak.prototype.deauthenticated = function (request) { // no-op -}; +} /** * Replaceable function to handle access-denied responses. @@ -299,91 +299,91 @@ Keycloak.prototype.deauthenticated = function (request) { * application would prefer to render a fancy template. */ Keycloak.prototype.accessDenied = function (request, response) { - response.status(403); - response.end('Access denied'); -}; + response.status(403) + response.end('Access denied') +} /*! ignore */ Keycloak.prototype.getGrant = function (request, response) { - let rawData; + let rawData for (let i = 0; i < this.stores.length; ++i) { - rawData = this.stores[i].get(request); + rawData = this.stores[i].get(request) if (rawData) { // store = this.stores[i]; - break; + break } } - let grantData = rawData; + let grantData = rawData if (typeof (grantData) === 'string') { - grantData = JSON.parse(grantData); + grantData = JSON.parse(grantData) } if (grantData && !grantData.error) { - const self = this; + const self = this return this.grantManager.createGrant(JSON.stringify(grantData)) .then(grant => { - self.storeGrant(grant, request, response); - return grant; + self.storeGrant(grant, request, response) + return grant }) - .catch(() => { return Promise.reject(new Error('Could not store grant code error')); }); + .catch(() => { return Promise.reject(new Error('Could not store grant code error')) }) } - return Promise.reject(new Error('Could not obtain grant code error')); -}; + return Promise.reject(new Error('Could not obtain grant code error')) +} Keycloak.prototype.storeGrant = function (grant, request, response) { if (this.stores.length < 2 || BearerStore.get(request)) { // cannot store bearer-only, and should not store if grant is from the // authorization header - return; + return } if (!grant) { - this.accessDenied(request, response); - return; + this.accessDenied(request, response) + return } - this.stores[1].wrap(grant); - grant.store(request, response); - return grant; -}; + this.stores[1].wrap(grant) + grant.store(request, response) + return grant +} Keycloak.prototype.unstoreGrant = function (sessionId) { if (this.stores.length < 2) { // cannot unstore, bearer-only, this is weird - return; + return } - this.stores[1].clear(sessionId); -}; + this.stores[1].clear(sessionId) +} Keycloak.prototype.getGrantFromCode = function (code, request, response) { if (this.stores.length < 2) { // bearer-only, cannot do this; - throw new Error('Cannot exchange code for grant in bearer-only mode'); + throw new Error('Cannot exchange code for grant in bearer-only mode') } - const sessionId = request.session.id; + const sessionId = request.session.id - const self = this; + const self = this return this.grantManager.obtainFromCode(request, code, sessionId) .then(function (grant) { - self.storeGrant(grant, request, response); - return grant; - }); -}; + self.storeGrant(grant, request, response) + return grant + }) +} Keycloak.prototype.checkPermissions = function (authzRequest, request, callback) { - const self = this; + const self = this return this.grantManager.checkPermissions(authzRequest, request, callback) .then(function (grant) { if (!authzRequest.response_mode) { - self.storeGrant(grant, request); + self.storeGrant(grant, request) } - return grant; - }); -}; + return grant + }) +} Keycloak.prototype.loginUrl = function (uuid, redirectUrl) { let url = this.config.realmUrl + @@ -392,39 +392,39 @@ Keycloak.prototype.loginUrl = function (uuid, redirectUrl) { '&state=' + encodeURIComponent(uuid) + '&redirect_uri=' + encodeURIComponent(redirectUrl) + '&scope=' + encodeURIComponent(this.config.scope ? 'openid ' + this.config.scope : 'openid') + - '&response_type=code'; + '&response_type=code' if (this.config && this.config.idpHint) { - url += '&kc_idp_hint=' + encodeURIComponent(this.config.idpHint); + url += '&kc_idp_hint=' + encodeURIComponent(this.config.idpHint) } - return url; -}; + return url +} Keycloak.prototype.logoutUrl = function (redirectUrl, idTokenHint) { - const url = new URL(this.config.realmUrl + '/protocol/openid-connect/logout'); + const url = new URL(this.config.realmUrl + '/protocol/openid-connect/logout') if (redirectUrl && idTokenHint) { - url.searchParams.set('id_token_hint', idTokenHint); - url.searchParams.set('post_logout_redirect_uri', redirectUrl); + url.searchParams.set('id_token_hint', idTokenHint) + url.searchParams.set('post_logout_redirect_uri', redirectUrl) } - return url.toString(); -}; + return url.toString() +} Keycloak.prototype.accountUrl = function () { - return this.config.realmUrl + '/account'; -}; + return this.config.realmUrl + '/account' +} Keycloak.prototype.getAccount = function (token) { - return this.grantManager.getAccount(token); -}; + return this.grantManager.getAccount(token) +} Keycloak.prototype.redirectToLogin = function (request) { - return !this.config.bearerOnly; -}; + return !this.config.bearerOnly +} Keycloak.prototype.getConfig = function () { - return this.config; -}; + return this.config +} -module.exports = Keycloak; +module.exports = Keycloak diff --git a/middleware/admin.js b/middleware/admin.js index f4240150..a9c81539 100644 --- a/middleware/admin.js +++ b/middleware/admin.js @@ -13,110 +13,110 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const Token = require('./auth-utils/token'); -const Signature = require('./auth-utils/signature'); +const Token = require('./auth-utils/token') +const Signature = require('./auth-utils/signature') function Admin (keycloak, url) { - this._keycloak = keycloak; + this._keycloak = keycloak if (url[url.length - 1] !== '/') { - url += '/;'; + url += '/;' } - this._url = url + 'k_logout'; + this._url = url + 'k_logout' } Admin.prototype.getFunction = function () { - return this._adminRequest.bind(this); -}; + return this._adminRequest.bind(this) +} function adminLogout (request, response, keycloak) { - let data = ''; + let data = '' request.on('data', d => { - data += d.toString(); - }); + data += d.toString() + }) request.on('end', function () { - const token = new Token(data); - let signature; + const token = new Token(data) + let signature try { - signature = new Signature(keycloak.config); + signature = new Signature(keycloak.config) signature.verify(token).then(token => { if (token.content.action === 'LOGOUT') { - const sessionIDs = token.content.adapterSessionIds; + const sessionIDs = token.content.adapterSessionIds if (!sessionIDs) { - keycloak.grantManager.notBefore = token.content.notBefore; - response.send('ok'); - return; + keycloak.grantManager.notBefore = token.content.notBefore + response.send('ok') + return } if (sessionIDs && sessionIDs.length > 0) { - let seen = 0; + let seen = 0 sessionIDs.forEach(id => { - keycloak.unstoreGrant(id); - ++seen; + keycloak.unstoreGrant(id) + ++seen if (seen === sessionIDs.length) { - response.send('ok'); + response.send('ok') } - }); + }) } else { - response.send('ok'); + response.send('ok') } } else { - response.status(400).end(); + response.status(400).end() } }).catch((err) => { - response.status(401).end(err.message); - }); + response.status(401).end(err.message) + }) } catch (err) { - response.status(400).end(err.message); + response.status(400).end(err.message) } - }); + }) } function adminNotBefore (request, response, keycloak) { - let data = ''; + let data = '' request.on('data', d => { - data += d.toString(); - }); + data += d.toString() + }) request.on('end', function () { - const token = new Token(data); - let signature; + const token = new Token(data) + let signature try { - signature = new Signature(keycloak.config); + signature = new Signature(keycloak.config) signature.verify(token).then(token => { if (token.content.action === 'PUSH_NOT_BEFORE') { - keycloak.grantManager.notBefore = token.content.notBefore; - response.send('ok'); + keycloak.grantManager.notBefore = token.content.notBefore + response.send('ok') } }).catch((err) => { - response.status(401).end(err.message); - }); + response.status(401).end(err.message) + }) } catch (err) { - response.status(400).end(err.message); + response.status(400).end(err.message) } - }); + }) } module.exports = function (keycloak, adminUrl) { - let url = adminUrl; + let url = adminUrl if (url[url.length - 1] !== '/') { - url = url + '/'; + url = url + '/' } - const urlLogout = url + 'k_logout'; - const urlNotBefore = url + 'k_push_not_before'; + const urlLogout = url + 'k_logout' + const urlNotBefore = url + 'k_push_not_before' return function adminRequest (request, response, next) { switch (request.url) { case urlLogout: - adminLogout(request, response, keycloak); - break; + adminLogout(request, response, keycloak) + break case urlNotBefore: - adminNotBefore(request, response, keycloak); - break; + adminNotBefore(request, response, keycloak) + break default: - return next(); + return next() } - }; -}; + } +} diff --git a/middleware/auth-utils/config.js b/middleware/auth-utils/config.js index 85e8f579..076e9512 100644 --- a/middleware/auth-utils/config.js +++ b/middleware/auth-utils/config.js @@ -14,10 +14,10 @@ * limitations under the License. */ -'use strict'; +'use strict' -const path = require('path'); -const fs = require('fs'); +const path = require('path') +const fs = require('fs') /** * Construct a configuration object. @@ -33,13 +33,13 @@ const fs = require('fs'); */ function Config (config) { if (!config) { - config = path.join(process.cwd(), 'keycloak.json'); + config = path.join(process.cwd(), 'keycloak.json') } if (typeof config === 'string') { - this.loadConfiguration(config); + this.loadConfiguration(config) } else { - this.configure(config); + this.configure(config) } } @@ -49,10 +49,10 @@ function Config (config) { * @param {String} configPath Path to a `keycloak.json` configuration. */ Config.prototype.loadConfiguration = function loadConfiguration (configPath) { - const json = fs.readFileSync(configPath); - const config = JSON.parse(json.toString()); - this.configure(config); -}; + const json = fs.readFileSync(configPath) + const config = JSON.parse(json.toString()) + this.configure(config) +} /** * Configure this `Config` object. @@ -75,98 +75,98 @@ Config.prototype.configure = function configure (config) { */ function resolveValue (value) { if (typeof value !== 'string') { - return value; + return value } // "${env.MY_ENVIRONMENT_VARIABLE:http://localhost:8080}".replace(/\$\{env\.([^:]*):?(.*)?\}/,"$1--split--$2").split("--split--") - const regex = /\$\{env\.([^:]*):?(.*)?\}/; + const regex = /\$\{env\.([^:]*):?(.*)?\}/ // is this an environment variable reference with potential fallback? if (!regex.test(value)) { - return value; + return value } - const tokens = value.replace(regex, '$1--split--$2').split('--split--'); - const envVar = tokens[0]; - const envVal = process.env[envVar]; - const fallbackVal = tokens[1]; + const tokens = value.replace(regex, '$1--split--$2').split('--split--') + const envVar = tokens[0] + const envVal = process.env[envVar] + const fallbackVal = tokens[1] - return envVal || fallbackVal; + return envVal || fallbackVal } /** * Realm ID * @type {String} */ - this.realm = resolveValue(config.realm || config.realm); + this.realm = resolveValue(config.realm || config.realm) /** * Client/Application ID * @type {String} */ - this.clientId = resolveValue(config.resource || config['client-id'] || config.clientId); + this.clientId = resolveValue(config.resource || config['client-id'] || config.clientId) /** * Client/Application secret * @type {String} */ - this.secret = resolveValue((config.credentials || {}).secret || config.secret); + this.secret = resolveValue((config.credentials || {}).secret || config.secret) /** * If this is a public application or confidential. * @type {String} */ - this.public = resolveValue(config['public-client'] || config.public || false); + this.public = resolveValue(config['public-client'] || config.public || false) /** * Authentication server URL * @type {String} */ - this.authServerUrl = (resolveValue(config['auth-server-url'] || config['server-url'] || config.serverUrl || config.authServerUrl) || '').replace(/\/*$/gi, ''); + this.authServerUrl = (resolveValue(config['auth-server-url'] || config['server-url'] || config.serverUrl || config.authServerUrl) || '').replace(/\/*$/gi, '') /** * Root realm URL. * @type {String} */ - this.realmUrl = this.authServerUrl + '/realms/' + this.realm; + this.realmUrl = this.authServerUrl + '/realms/' + this.realm /** * Root realm admin URL. * @type {String} */ - this.realmAdminUrl = this.authServerUrl + '/admin/realms/' + this.realm; + this.realmAdminUrl = this.authServerUrl + '/admin/realms/' + this.realm /** * How many minutes before retrying getting the keys. * @type {Integer} */ - this.minTimeBetweenJwksRequests = config['min-time-between-jwks-requests'] || config.minTimeBetweenJwksRequests || 10; + this.minTimeBetweenJwksRequests = config['min-time-between-jwks-requests'] || config.minTimeBetweenJwksRequests || 10 /** * If this is a Bearer Only application. * @type {Boolean} */ - this.bearerOnly = resolveValue(config['bearer-only'] || config.bearerOnly || false); + this.bearerOnly = resolveValue(config['bearer-only'] || config.bearerOnly || false) /** * Formatted public-key. * @type {String} */ - const plainKey = resolveValue(config['realm-public-key'] || config.realmPublicKey); + const plainKey = resolveValue(config['realm-public-key'] || config.realmPublicKey) if (plainKey) { - this.publicKey = '-----BEGIN PUBLIC KEY-----\n'; + this.publicKey = '-----BEGIN PUBLIC KEY-----\n' for (let i = 0; i < plainKey.length; i = i + 64) { - this.publicKey += plainKey.substring(i, i + 64); - this.publicKey += '\n'; + this.publicKey += plainKey.substring(i, i + 64) + this.publicKey += '\n' } - this.publicKey += '-----END PUBLIC KEY-----'; + this.publicKey += '-----END PUBLIC KEY-----' } /** * Verify token audience * @type {Boolean} */ - this.verifyTokenAudience = resolveValue(config['verify-token-audience'] || config.verifyTokenAudience || false); -}; + this.verifyTokenAudience = resolveValue(config['verify-token-audience'] || config.verifyTokenAudience || false) +} -module.exports = Config; +module.exports = Config diff --git a/middleware/auth-utils/grant-manager.js b/middleware/auth-utils/grant-manager.js index 387a962e..09054d35 100644 --- a/middleware/auth-utils/grant-manager.js +++ b/middleware/auth-utils/grant-manager.js @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -'use strict'; +'use strict' -const URL = require('url'); -const http = require('http'); -const https = require('https'); -const crypto = require('crypto'); -const querystring = require('querystring'); -const Grant = require('./grant'); -const Token = require('./token'); -const Rotation = require('./rotation'); +const URL = require('url') +const http = require('http') +const https = require('https') +const crypto = require('crypto') +const querystring = require('querystring') +const Grant = require('./grant') +const Token = require('./token') +const Rotation = require('./rotation') /** * Construct a grant manager. @@ -32,15 +32,15 @@ const Rotation = require('./rotation'); * @constructor */ function GrantManager (config) { - this.realmUrl = config.realmUrl; - this.clientId = config.clientId; - this.secret = config.secret; - this.publicKey = config.publicKey; - this.public = config.public; - this.bearerOnly = config.bearerOnly; - this.notBefore = 0; - this.rotation = new Rotation(config); - this.verifyTokenAudience = config.verifyTokenAudience; + this.realmUrl = config.realmUrl + this.clientId = config.clientId + this.secret = config.secret + this.publicKey = config.publicKey + this.public = config.public + this.bearerOnly = config.bearerOnly + this.notBefore = 0 + this.rotation = new Rotation(config) + this.verifyTokenAudience = config.verifyTokenAudience } /** @@ -61,15 +61,15 @@ GrantManager.prototype.obtainDirectly = function obtainDirectly (username, passw callback, scopeParam) { const params = { client_id: this.clientId, - username: username, - password: password, + username, + password, grant_type: 'password', scope: scopeParam || 'openid' - }; - const handler = createHandler(this); - const options = postOptions(this); - return nodeify(fetch(this, handler, options, params), callback); -}; + } + const handler = createHandler(this) + const options = postOptions(this) + return nodeify(fetch(this, handler, options, params), callback) +} /** * Obtain a grant from a previous interactive login which results in a code. @@ -93,107 +93,107 @@ GrantManager.prototype.obtainFromCode = function obtainFromCode (request, code, const params = { client_session_state: sessionId, client_session_host: sessionHost, - code: code, + code, grant_type: 'authorization_code', client_id: this.clientId, redirect_uri: request.session ? request.session.auth_redirect_uri : {} - }; - const handler = createHandler(this); - const options = postOptions(this); + } + const handler = createHandler(this) + const options = postOptions(this) - return nodeify(fetch(this, handler, options, params), callback); -}; + return nodeify(fetch(this, handler, options, params), callback) +} GrantManager.prototype.checkPermissions = function obtainPermissions (authzRequest, request, callback) { const params = { grant_type: 'urn:ietf:params:oauth:grant-type:uma-ticket' - }; + } if (authzRequest.audience) { - params.audience = authzRequest.audience; + params.audience = authzRequest.audience } else { - params.audience = this.clientId; + params.audience = this.clientId } if (authzRequest.response_mode) { - params.response_mode = authzRequest.response_mode; + params.response_mode = authzRequest.response_mode } if (authzRequest.claim_token) { - params.claim_token = authzRequest.claim_token; - params.claim_token_format = authzRequest.claim_token_format; + params.claim_token = authzRequest.claim_token + params.claim_token_format = authzRequest.claim_token_format } - const options = postOptions(this); + const options = postOptions(this) if (this.public) { if (request.kauth && request.kauth.grant && request.kauth.grant.access_token) { - options.headers.Authorization = 'Bearer ' + request.kauth.grant.access_token.token; + options.headers.Authorization = 'Bearer ' + request.kauth.grant.access_token.token } } else { - const header = request.headers.authorization; - let bearerToken; + const header = request.headers.authorization + let bearerToken if (header && (header.indexOf('bearer ') === 0 || header.indexOf('Bearer ') === 0)) { - bearerToken = header.substring(7); + bearerToken = header.substring(7) } if (!bearerToken) { if (request.kauth && request.kauth.grant && request.kauth.grant.access_token) { - bearerToken = request.kauth.grant.access_token.token; + bearerToken = request.kauth.grant.access_token.token } else { - return Promise.reject(new Error('No bearer in header')); + return Promise.reject(new Error('No bearer in header')) } } - params.subject_token = bearerToken; + params.subject_token = bearerToken } - let permissions = authzRequest.permissions; + let permissions = authzRequest.permissions if (!permissions) { - permissions = []; + permissions = [] } for (let i = 0; i < permissions.length; i++) { - const resource = permissions[i]; - let permission = resource.id; + const resource = permissions[i] + let permission = resource.id if (resource.scopes && resource.scopes.length > 0) { - permission += '#'; + permission += '#' for (let j = 0; j < resource.scopes.length; j++) { - const scope = resource.scopes[j]; + const scope = resource.scopes[j] if (permission.indexOf('#') !== permission.length - 1) { - permission += ','; + permission += ',' } - permission += scope; + permission += scope } } if (!params.permission) { - params.permission = []; + params.permission = [] } - params.permission.push(permission); + params.permission.push(permission) } - const manager = this; + const manager = this const handler = (resolve, reject, json) => { try { if (authzRequest.response_mode === 'decision' || authzRequest.response_mode === 'permissions') { - callback(JSON.parse(json)); + callback(JSON.parse(json)) } else { - resolve(manager.createGrant(json)); + resolve(manager.createGrant(json)) } } catch (err) { - reject(err); + reject(err) } - }; + } - return nodeify(fetch(this, handler, options, params)); -}; + return nodeify(fetch(this, handler, options, params)) +} /** * Obtain a service account grant. @@ -208,12 +208,12 @@ GrantManager.prototype.obtainFromClientCredentials = function obtainFromlientCre grant_type: 'client_credentials', scope: scopeParam || 'openid', client_id: this.clientId - }; - const handler = createHandler(this); - const options = postOptions(this); + } + const handler = createHandler(this) + const options = postOptions(this) - return nodeify(fetch(this, handler, options, params), callback); -}; + return nodeify(fetch(this, handler, options, params), callback) +} /** * Ensure that a grant is *fresh*, refreshing if required & possible. @@ -234,27 +234,27 @@ GrantManager.prototype.obtainFromClientCredentials = function obtainFromlientCre */ GrantManager.prototype.ensureFreshness = function ensureFreshness (grant, callback) { if (!grant.isExpired()) { - return nodeify(Promise.resolve(grant), callback); + return nodeify(Promise.resolve(grant), callback) } if (!grant.refresh_token) { - return nodeify(Promise.reject(new Error('Unable to refresh without a refresh token')), callback); + return nodeify(Promise.reject(new Error('Unable to refresh without a refresh token')), callback) } if (grant.refresh_token.isExpired()) { - return nodeify(Promise.reject(new Error('Unable to refresh with expired refresh token')), callback); + return nodeify(Promise.reject(new Error('Unable to refresh with expired refresh token')), callback) } const params = { grant_type: 'refresh_token', refresh_token: grant.refresh_token.token, client_id: this.clientId - }; - const handler = refreshHandler(this, grant); - const options = postOptions(this); + } + const handler = refreshHandler(this, grant) + const options = postOptions(this) - return nodeify(fetch(this, handler, options, params), callback); -}; + return nodeify(fetch(this, handler, options, params), callback) +} /** * Perform live validation of an `access_token` against the Keycloak server. @@ -265,63 +265,63 @@ GrantManager.prototype.ensureFreshness = function ensureFreshness (grant, callba * @return {boolean} `false` if the token is invalid, or the same token if valid. */ GrantManager.prototype.validateAccessToken = function validateAccessToken (token, callback) { - let t = token; + let t = token if (typeof token === 'object') { - t = token.token; + t = token.token } const params = { token: t, client_secret: this.secret, client_id: this.clientId - }; - const options = postOptions(this, '/protocol/openid-connect/token/introspect'); - const handler = validationHandler(this, token); + } + const options = postOptions(this, '/protocol/openid-connect/token/introspect') + const handler = validationHandler(this, token) - return nodeify(fetch(this, handler, options, params), callback); -}; + return nodeify(fetch(this, handler, options, params), callback) +} GrantManager.prototype.userInfo = function userInfo (token, callback) { - const url = this.realmUrl + '/protocol/openid-connect/userinfo'; + const url = this.realmUrl + '/protocol/openid-connect/userinfo' const options = URL.parse(url); // eslint-disable-line - options.method = 'GET'; + options.method = 'GET' - let t = token; - if (typeof token === 'object') t = token.token; + let t = token + if (typeof token === 'object') t = token.token options.headers = { Authorization: 'Bearer ' + t, Accept: 'application/json', 'X-Client': 'keycloak-nodejs-connect' - }; + } const promise = new Promise((resolve, reject) => { const req = getProtocol(options).request(options, (response) => { if (response.statusCode < 200 || response.statusCode >= 300) { - return reject(new Error('Error fetching account')); + return reject(new Error('Error fetching account')) } - let json = ''; - response.on('data', (d) => (json += d.toString())); + let json = '' + response.on('data', (d) => (json += d.toString())) response.on('end', () => { - const data = JSON.parse(json); - if (data.error) reject(data); - else resolve(data); - }); - }); - req.on('error', reject); - req.end(); - }); - - return nodeify(promise, callback); -}; + const data = JSON.parse(json) + if (data.error) reject(data) + else resolve(data) + }) + }) + req.on('error', reject) + req.end() + }) + + return nodeify(promise, callback) +} GrantManager.prototype.getAccount = function getAccount () { - console.error('GrantManager#getAccount is deprecated. See GrantManager#userInfo'); - return this.userInfo.apply(this, arguments); -}; + console.error('GrantManager#getAccount is deprecated. See GrantManager#userInfo') + return this.userInfo.apply(this, arguments) +} GrantManager.prototype.isGrantRefreshable = function isGrantRefreshable (grant) { - return !this.bearerOnly && (grant && grant.refresh_token); -}; + return !this.bearerOnly && (grant && grant.refresh_token) +} /** * Create a `Grant` object from a string of JSON data. @@ -335,8 +335,8 @@ GrantManager.prototype.isGrantRefreshable = function isGrantRefreshable (grant) * @return {Promise} A promise reoslving a grant. */ GrantManager.prototype.createGrant = function createGrant (rawData) { - let grantData = rawData; - if (typeof rawData !== 'object') grantData = JSON.parse(grantData); + let grantData = rawData + if (typeof rawData !== 'object') grantData = JSON.parse(grantData) const grant = new Grant({ access_token: (grantData.access_token ? new Token(grantData.access_token, this.clientId) : undefined), @@ -345,19 +345,19 @@ GrantManager.prototype.createGrant = function createGrant (rawData) { expires_in: grantData.expires_in, token_type: grantData.token_type, __raw: rawData - }); + }) if (this.isGrantRefreshable(grant)) { return new Promise((resolve, reject) => { this.ensureFreshness(grant) .then(g => this.validateGrant(g)) .then(g => resolve(g)) - .catch(err => reject(err)); - }); + .catch(err => reject(err)) + }) } else { - return this.validateGrant(grant); + return this.validateGrant(grant) } -}; +} /** * Validate the grant and all tokens contained therein. @@ -373,33 +373,33 @@ GrantManager.prototype.createGrant = function createGrant (rawData) { * rejects with an error if any of the tokens are invalid. */ GrantManager.prototype.validateGrant = function validateGrant (grant) { - const self = this; + const self = this const validateGrantToken = (grant, tokenName, expectedType) => { return new Promise((resolve, reject) => { // check the access token this.validateToken(grant[tokenName], expectedType).then(token => { - grant[tokenName] = token; - resolve(); + grant[tokenName] = token + resolve() }).catch((err) => { - reject(new Error('Grant validation failed. Reason: ' + err.message)); - }); - }); - }; + reject(new Error('Grant validation failed. Reason: ' + err.message)) + }) + }) + } return new Promise((resolve, reject) => { - const promises = []; - promises.push(validateGrantToken(grant, 'access_token', 'Bearer')); + const promises = [] + promises.push(validateGrantToken(grant, 'access_token', 'Bearer')) if (!self.bearerOnly) { if (grant.id_token) { - promises.push(validateGrantToken(grant, 'id_token', 'ID')); + promises.push(validateGrantToken(grant, 'id_token', 'ID')) } } Promise.all(promises).then(() => { - resolve(grant); + resolve(grant) }).catch((err) => { - reject(new Error(err.message)); - }); - }); -}; + reject(new Error(err.message)) + }) + }) +} /** * Validate a token. @@ -415,124 +415,124 @@ GrantManager.prototype.validateGrant = function validateGrant (grant) { GrantManager.prototype.validateToken = function validateToken (token, expectedType) { return new Promise((resolve, reject) => { if (!token) { - reject(new Error('invalid token (missing)')); + reject(new Error('invalid token (missing)')) } else if (token.isExpired()) { - reject(new Error('invalid token (expired)')); + reject(new Error('invalid token (expired)')) } else if (!token.signed) { - reject(new Error('invalid token (not signed)')); + reject(new Error('invalid token (not signed)')) } else if (token.content.typ !== expectedType) { - reject(new Error('invalid token (wrong type)')); + reject(new Error('invalid token (wrong type)')) } else if (token.content.iat < this.notBefore) { - reject(new Error('invalid token (stale token)')); + reject(new Error('invalid token (stale token)')) } else if (token.content.iss !== this.realmUrl) { - reject(new Error('invalid token (wrong ISS)')); + reject(new Error('invalid token (wrong ISS)')) } else { - const audienceData = Array.isArray(token.content.aud) ? token.content.aud : [token.content.aud]; + const audienceData = Array.isArray(token.content.aud) ? token.content.aud : [token.content.aud] if (expectedType === 'ID') { if (!audienceData.includes(this.clientId)) { - reject(new Error('invalid token (wrong audience)')); + reject(new Error('invalid token (wrong audience)')) } if (token.content.azp && token.content.azp !== this.clientId) { - reject(new Error('invalid token (authorized party should match client id)')); + reject(new Error('invalid token (authorized party should match client id)')) } } else if (this.verifyTokenAudience) { if (!audienceData.includes(this.clientId)) { - reject(new Error('invalid token (wrong audience)')); + reject(new Error('invalid token (wrong audience)')) } } - const verify = crypto.createVerify('RSA-SHA256'); + const verify = crypto.createVerify('RSA-SHA256') // if public key has been supplied use it to validate token if (this.publicKey) { try { - verify.update(token.signed); + verify.update(token.signed) if (!verify.verify(this.publicKey, token.signature, 'base64')) { - reject(new Error('invalid token (signature)')); + reject(new Error('invalid token (signature)')) } else { - resolve(token); + resolve(token) } } catch (err) { - reject(new Error('Misconfigured parameters while validating token. Check your keycloak.json file!')); + reject(new Error('Misconfigured parameters while validating token. Check your keycloak.json file!')) } } else { // retrieve public KEY and use it to validate token this.rotation.getJWK(token.header.kid).then(key => { - verify.update(token.signed); + verify.update(token.signed) if (!verify.verify(key, token.signature)) { - reject(new Error('invalid token (public key signature)')); + reject(new Error('invalid token (public key signature)')) } else { - resolve(token); + resolve(token) } }).catch((err) => { - reject(new Error('failed to load public key to verify token. Reason: ' + err.message)); - }); + reject(new Error('failed to load public key to verify token. Reason: ' + err.message)) + }) } } - }); -}; + }) +} const getProtocol = (opts) => { - return opts.protocol === 'https:' ? https : http; -}; + return opts.protocol === 'https:' ? https : http +} const nodeify = (promise, cb) => { - if (typeof cb !== 'function') return promise; - return promise.then((res) => cb(null, res)).catch((err) => cb(err)); -}; + if (typeof cb !== 'function') return promise + return promise.then((res) => cb(null, res)).catch((err) => cb(err)) +} const createHandler = (manager) => (resolve, reject, json) => { try { - resolve(manager.createGrant(json)); + resolve(manager.createGrant(json)) } catch (err) { - reject(err); + reject(err) } -}; +} const refreshHandler = (manager, grant) => (resolve, reject, json) => { manager.createGrant(json) .then((grant) => resolve(grant)) - .catch((err) => reject(err)); -}; + .catch((err) => reject(err)) +} const validationHandler = (manager, token) => (resolve, reject, json) => { - const data = JSON.parse(json); - if (!data.active) resolve(false); - else resolve(token); -}; + const data = JSON.parse(json) + if (!data.active) resolve(false) + else resolve(token) +} const postOptions = (manager, path) => { - const realPath = path || '/protocol/openid-connect/token'; + const realPath = path || '/protocol/openid-connect/token' const opts = URL.parse(manager.realmUrl + realPath); // eslint-disable-line opts.headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Client': 'keycloak-nodejs-connect' - }; + } if (!manager.public) { - opts.headers.Authorization = 'Basic ' + Buffer.from(manager.clientId + ':' + manager.secret).toString('base64'); + opts.headers.Authorization = 'Basic ' + Buffer.from(manager.clientId + ':' + manager.secret).toString('base64') } - opts.method = 'POST'; - return opts; -}; + opts.method = 'POST' + return opts +} const fetch = (manager, handler, options, params) => { return new Promise((resolve, reject) => { - const data = (typeof params === 'string' ? params : querystring.stringify(params)); - options.headers['Content-Length'] = data.length; + const data = (typeof params === 'string' ? params : querystring.stringify(params)) + options.headers['Content-Length'] = data.length const req = getProtocol(options).request(options, (response) => { if (response.statusCode < 200 || response.statusCode > 299) { - return reject(new Error(response.statusCode + ':' + http.STATUS_CODES[response.statusCode])); + return reject(new Error(response.statusCode + ':' + http.STATUS_CODES[response.statusCode])) } - let json = ''; - response.on('data', (d) => (json += d.toString())); + let json = '' + response.on('data', (d) => (json += d.toString())) response.on('end', () => { - handler(resolve, reject, json); - }); - }); - - req.write(data); - req.on('error', reject); - req.end(); - }); -}; + handler(resolve, reject, json) + }) + }) + + req.write(data) + req.on('error', reject) + req.end() + }) +} -module.exports = GrantManager; +module.exports = GrantManager diff --git a/middleware/auth-utils/grant.js b/middleware/auth-utils/grant.js index 16569094..854ef25d 100644 --- a/middleware/auth-utils/grant.js +++ b/middleware/auth-utils/grant.js @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -'use strict'; +'use strict' /** * Construct a new grant. @@ -31,7 +31,7 @@ * @constructor */ function Grant (grant) { - this.update(grant); + this.update(grant) } /** @@ -45,14 +45,14 @@ Grant.prototype.update = function update (grant) { // CamelCase to match both Keycloak's grant JSON // and to allow new Grant(new Grant(kc)) copy-ctor - this.access_token = grant.access_token; - this.refresh_token = grant.refresh_token; - this.id_token = grant.id_token; + this.access_token = grant.access_token + this.refresh_token = grant.refresh_token + this.id_token = grant.id_token - this.token_type = grant.token_type; - this.expires_in = grant.expires_in; - this.__raw = grant.__raw; -}; + this.token_type = grant.token_type + this.expires_in = grant.expires_in + this.__raw = grant.__raw +} /** * Returns the raw String of the grant, if available. @@ -61,8 +61,8 @@ Grant.prototype.update = function update (grant) { * then `undefined` is returned. */ Grant.prototype.toString = function toString () { - return this.__raw; -}; + return this.__raw +} /** * Determine if this grant is expired/out-of-date. @@ -76,9 +76,9 @@ Grant.prototype.toString = function toString () { */ Grant.prototype.isExpired = function isExpired () { if (!this.access_token) { - return true; + return true } - return this.access_token.isExpired(); -}; + return this.access_token.isExpired() +} -module.exports = Grant; +module.exports = Grant diff --git a/middleware/auth-utils/rotation.js b/middleware/auth-utils/rotation.js index 13fcc822..838fa08e 100644 --- a/middleware/auth-utils/rotation.js +++ b/middleware/auth-utils/rotation.js @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -'use strict'; -const URL = require('url'); -const http = require('http'); -const https = require('https'); -const jwkToPem = require('jwk-to-pem'); +'use strict' +const URL = require('url') +const http = require('http') +const https = require('https') +const jwkToPem = require('jwk-to-pem') /** * Construct a Rotation instance @@ -27,70 +27,70 @@ const jwkToPem = require('jwk-to-pem'); * @constructor */ function Rotation (config) { - this.realmUrl = config.realmUrl; - this.minTimeBetweenJwksRequests = config.minTimeBetweenJwksRequests; - this.jwks = []; - this.lastTimeRequesTime = 0; + this.realmUrl = config.realmUrl + this.minTimeBetweenJwksRequests = config.minTimeBetweenJwksRequests + this.jwks = [] + this.lastTimeRequesTime = 0 } Rotation.prototype.retrieveJWKs = function retrieveJWKs (callback) { - const url = this.realmUrl + '/protocol/openid-connect/certs'; + const url = this.realmUrl + '/protocol/openid-connect/certs' const options = URL.parse(url); // eslint-disable-line - options.method = 'GET'; + options.method = 'GET' const promise = new Promise((resolve, reject) => { const req = getProtocol(options).request(options, (response) => { if (response.statusCode < 200 || response.statusCode >= 300) { - return reject(new Error('Error fetching JWK Keys')); + return reject(new Error('Error fetching JWK Keys')) } - let json = ''; - response.on('data', (d) => (json += d.toString())); + let json = '' + response.on('data', (d) => (json += d.toString())) response.on('end', () => { - const data = JSON.parse(json); - if (data.error) reject(data); - else resolve(data); - }); - }); - req.on('error', reject); - req.end(); - }); - return nodeify(promise, callback); -}; + const data = JSON.parse(json) + if (data.error) reject(data) + else resolve(data) + }) + }) + req.on('error', reject) + req.end() + }) + return nodeify(promise, callback) +} Rotation.prototype.getJWK = function getJWK (kid) { - const key = this.jwks.find((key) => { return key.kid === kid; }); + const key = this.jwks.find((key) => { return key.kid === kid }) if (key) { return new Promise((resolve, reject) => { - resolve(jwkToPem(key)); - }); + resolve(jwkToPem(key)) + }) } - const self = this; + const self = this // check if we are allowed to send request - const currentTime = new Date().getTime() / 1000; + const currentTime = new Date().getTime() / 1000 if (currentTime > this.lastTimeRequesTime + this.minTimeBetweenJwksRequests) { return this.retrieveJWKs() .then(publicKeys => { - self.lastTimeRequesTime = currentTime; - self.jwks = publicKeys.keys; - const convertedKey = jwkToPem(self.jwks.find((key) => { return key.kid === kid; })); - return convertedKey; - }); + self.lastTimeRequesTime = currentTime + self.jwks = publicKeys.keys + const convertedKey = jwkToPem(self.jwks.find((key) => { return key.kid === kid })) + return convertedKey + }) } else { - console.error('Not enough time elapsed since the last request, blocking the request'); + console.error('Not enough time elapsed since the last request, blocking the request') } -}; +} Rotation.prototype.clearCache = function clearCache () { - this.jwks.length = 0; -}; + this.jwks.length = 0 +} const getProtocol = (opts) => { - return opts.protocol === 'https:' ? https : http; -}; + return opts.protocol === 'https:' ? https : http +} const nodeify = (promise, cb) => { - if (typeof cb !== 'function') return promise; - return promise.then((res) => cb(null, res)).catch((err) => cb(err)); -}; + if (typeof cb !== 'function') return promise + return promise.then((res) => cb(null, res)).catch((err) => cb(err)) +} -module.exports = Rotation; +module.exports = Rotation diff --git a/middleware/auth-utils/signature.js b/middleware/auth-utils/signature.js index 87d55ac1..4967f740 100644 --- a/middleware/auth-utils/signature.js +++ b/middleware/auth-utils/signature.js @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -'use strict'; +'use strict' -const Rotation = require('./rotation'); -const crypto = require('crypto'); +const Rotation = require('./rotation') +const crypto = require('crypto') /** * Construct a signature. @@ -26,8 +26,8 @@ const crypto = require('crypto'); * @constructor */ function Signature (config) { - this.publicKey = config.publicKey; - this.rotation = new Rotation(config); + this.publicKey = config.publicKey + this.rotation = new Rotation(config) } /** @@ -37,19 +37,19 @@ function Signature (config) { */ Signature.prototype.verify = function verify (token, callback) { return new Promise((resolve, reject) => { - const verify = crypto.createVerify('RSA-SHA256'); + const verify = crypto.createVerify('RSA-SHA256') this.rotation.getJWK(token.header.kid).then(key => { - verify.update(token.signed); + verify.update(token.signed) if (!verify.verify(key, token.signature, 'base64')) { - reject(new Error('admin request failed: invalid token (signature)')); + reject(new Error('admin request failed: invalid token (signature)')) } else { - resolve(token); + resolve(token) } }).catch((err) => { - reject(new Error('failed to load public key to verify token. Reason: ' + err.message)); - }); - }); -}; + reject(new Error('failed to load public key to verify token. Reason: ' + err.message)) + }) + }) +} -module.exports = Signature; +module.exports = Signature diff --git a/middleware/auth-utils/token.js b/middleware/auth-utils/token.js index e76a8510..517e77d3 100644 --- a/middleware/auth-utils/token.js +++ b/middleware/auth-utils/token.js @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -'use strict'; +'use strict' /** * Construct a token. @@ -28,20 +28,20 @@ * @param {String} clientId Optional clientId if this is an `access_token`. */ function Token (token, clientId) { - this.token = token; - this.clientId = clientId; + this.token = token + this.clientId = clientId if (token) { try { - const parts = token.split('.'); - this.header = JSON.parse(Buffer.from(parts[0], 'base64').toString()); - this.content = JSON.parse(Buffer.from(parts[1], 'base64').toString()); - this.signature = Buffer.from(parts[2], 'base64'); - this.signed = parts[0] + '.' + parts[1]; + const parts = token.split('.') + this.header = JSON.parse(Buffer.from(parts[0], 'base64').toString()) + this.content = JSON.parse(Buffer.from(parts[1], 'base64').toString()) + this.signature = Buffer.from(parts[2], 'base64') + this.signed = parts[0] + '.' + parts[1] } catch (err) { this.content = { exp: 0 - }; + } } } } @@ -52,8 +52,8 @@ function Token (token, clientId) { * @return {boolean} `true` if it is expired, otherwise `false`. */ Token.prototype.isExpired = function isExpired () { - return ((this.content.exp * 1000) < Date.now()); -}; + return ((this.content.exp * 1000) < Date.now()) +} /** * Determine if this token has an associated role. @@ -78,20 +78,20 @@ Token.prototype.isExpired = function isExpired () { */ Token.prototype.hasRole = function hasRole (name) { if (!this.clientId) { - return false; + return false } - const parts = name.split(':'); + const parts = name.split(':') if (parts.length === 1) { - return this.hasApplicationRole(this.clientId, parts[0]); + return this.hasApplicationRole(this.clientId, parts[0]) } if (parts[0] === 'realm') { - return this.hasRealmRole(parts[1]); + return this.hasRealmRole(parts[1]) } - return this.hasApplicationRole(parts[0], parts[1]); -}; + return this.hasApplicationRole(parts[0], parts[1]) +} /** * Determine if this token has an associated specific application role. @@ -106,17 +106,17 @@ Token.prototype.hasRole = function hasRole (name) { */ Token.prototype.hasApplicationRole = function hasApplicationRole (appName, roleName) { if (!this.content.resource_access) { - return false; + return false } - const appRoles = this.content.resource_access[appName]; + const appRoles = this.content.resource_access[appName] if (!appRoles) { - return false; + return false } - return (appRoles.roles.indexOf(roleName) >= 0); -}; + return (appRoles.roles.indexOf(roleName) >= 0) +} /** * Determine if this token has an associated specific realm-level role. @@ -134,11 +134,11 @@ Token.prototype.hasRealmRole = function hasRealmRole (roleName) { // Without this we attempt to access an undefined property on token // for a user with no realm level roles. if (!this.content.realm_access || !this.content.realm_access.roles) { - return false; + return false } - return (this.content.realm_access.roles.indexOf(roleName) >= 0); -}; + return (this.content.realm_access.roles.indexOf(roleName) >= 0) +} /** * Determine if this token has an associated role. @@ -162,28 +162,28 @@ Token.prototype.hasRealmRole = function hasRealmRole (roleName) { * @return {boolean} `true` if this token has the specified role, otherwise `false`. */ Token.prototype.hasPermission = function hasPermission (resource, scope) { - const permissions = this.content.authorization ? this.content.authorization.permissions : undefined; + const permissions = this.content.authorization ? this.content.authorization.permissions : undefined if (!permissions) { - return false; + return false } for (let i = 0; i < permissions.length; i++) { - const permission = permissions[i]; + const permission = permissions[i] if (permission.rsid === resource || permission.rsname === resource) { if (scope) { if (permission.scopes && permission.scopes.length > 0) { if (!permission.scopes.includes(scope)) { - return false; + return false } } } - return true; + return true } } - return false; -}; + return false +} -module.exports = Token; +module.exports = Token diff --git a/middleware/check-sso.js b/middleware/check-sso.js index a7cbd844..a28b0035 100644 --- a/middleware/check-sso.js +++ b/middleware/check-sso.js @@ -13,41 +13,41 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const UUID = require('./../uuid'); -const URL = require('url'); +const UUID = require('./../uuid') +const URL = require('url') function forceCheckSSO (keycloak, request, response) { - const host = request.hostname; - const headerHost = request.headers.host.split(':'); - const port = headerHost[1] || ''; - const protocol = request.protocol; - const hasQuery = ~(request.originalUrl || request.url).indexOf('?'); + const host = request.hostname + const headerHost = request.headers.host.split(':') + const port = headerHost[1] || '' + const protocol = request.protocol + const hasQuery = ~(request.originalUrl || request.url).indexOf('?') - const redirectUrl = protocol + '://' + host + (port === '' ? '' : ':' + port) + (request.originalUrl || request.url) + (hasQuery ? '&' : '?') + 'auth_callback=1'; + const redirectUrl = protocol + '://' + host + (port === '' ? '' : ':' + port) + (request.originalUrl || request.url) + (hasQuery ? '&' : '?') + 'auth_callback=1' if (request.session) { - request.session.auth_redirect_uri = redirectUrl; + request.session.auth_redirect_uri = redirectUrl } - const uuid = UUID(); - const loginURL = keycloak.loginUrl(uuid, redirectUrl); - const checkSsoUrl = loginURL + '&response_mode=query&prompt=none'; + const uuid = UUID() + const loginURL = keycloak.loginUrl(uuid, redirectUrl) + const checkSsoUrl = loginURL + '&response_mode=query&prompt=none' - response.redirect(checkSsoUrl); + response.redirect(checkSsoUrl) } module.exports = function (keycloak) { return function checkSso (request, response, next) { if (request.kauth && request.kauth.grant) { - return next(); + return next() } // Check SSO process is completed and user is not logged in if (request.session.auth_is_check_sso_complete) { - request.session.auth_is_check_sso_complete = false; - return next(); + request.session.auth_is_check_sso_complete = false + return next() } // Keycloak server has just answered that user is not logged in @@ -55,25 +55,25 @@ module.exports = function (keycloak) { const urlParts = { pathname: request.path, query: request.query - }; + } - delete urlParts.query.error; - delete urlParts.query.auth_callback; - delete urlParts.query.state; + delete urlParts.query.error + delete urlParts.query.auth_callback + delete urlParts.query.state - const cleanUrl = URL.format(urlParts); + const cleanUrl = URL.format(urlParts) // Check SSO process is completed - request.session.auth_is_check_sso_complete = true; + request.session.auth_is_check_sso_complete = true // Redirect back to the original URL - return response.redirect(cleanUrl); + return response.redirect(cleanUrl) } if (keycloak.redirectToLogin(request)) { - forceCheckSSO(keycloak, request, response); + forceCheckSSO(keycloak, request, response) } else { - return keycloak.accessDenied(request, response, next); + return keycloak.accessDenied(request, response, next) } - }; -}; + } +} diff --git a/middleware/enforcer.js b/middleware/enforcer.js index f25b583f..b7c1e963 100644 --- a/middleware/enforcer.js +++ b/middleware/enforcer.js @@ -13,26 +13,26 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' function handlePermissions (permissions, callback) { for (let i = 0; i < permissions.length; i++) { - const expected = permissions[i].split(':'); - const resource = expected[0]; - let scope; + const expected = permissions[i].split(':') + const resource = expected[0] + let scope if (expected.length > 1) { - scope = expected[1]; + scope = expected[1] } - const r = callback(resource, scope); + const r = callback(resource, scope) if (r === false) { - return r; + return r } } - return true; + return true } /** @@ -43,66 +43,66 @@ function handlePermissions (permissions, callback) { * @constructor */ function Enforcer (keycloak, config) { - this.keycloak = keycloak; - this.config = config || {}; + this.keycloak = keycloak + this.config = config || {} if (!this.config.response_mode) { - this.config.response_mode = 'permissions'; + this.config.response_mode = 'permissions' } if (!this.config.resource_server_id) { - this.config.resource_server_id = this.keycloak.getConfig().clientId; + this.config.resource_server_id = this.keycloak.getConfig().clientId } } Enforcer.prototype.enforce = function enforce (expectedPermissions) { - const keycloak = this.keycloak; - const config = this.config; + const keycloak = this.keycloak + const config = this.config if (typeof expectedPermissions === 'string') { - expectedPermissions = [expectedPermissions]; + expectedPermissions = [expectedPermissions] } return function (request, response, next) { if (!expectedPermissions || expectedPermissions.length === 0) { - return next(); + return next() } const authzRequest = { audience: config.resource_server_id, response_mode: config.response_mode - }; + } handlePermissions(expectedPermissions, function (resource, scope) { if (!authzRequest.permissions) { - authzRequest.permissions = []; + authzRequest.permissions = [] } - const permission = { id: resource }; + const permission = { id: resource } if (scope) { - permission.scopes = [scope]; + permission.scopes = [scope] } - authzRequest.permissions.push(permission); - }); + authzRequest.permissions.push(permission) + }) if (request.kauth && request.kauth.grant) { if (handlePermissions(expectedPermissions, function (resource, scope) { if (!request.kauth.grant.access_token.hasPermission(resource, scope)) { - return false; + return false } })) { - return next(); + return next() } } if (config.claims) { - const claims = config.claims(request); + const claims = config.claims(request) if (claims) { - authzRequest.claim_token = Buffer.from(JSON.stringify(claims)).toString('base64'); - authzRequest.claim_token_format = 'urn:ietf:params:oauth:token-type:jwt'; + authzRequest.claim_token = Buffer.from(JSON.stringify(claims)).toString('base64') + authzRequest.claim_token_format = 'urn:ietf:params:oauth:token-type:jwt' } } @@ -110,50 +110,50 @@ Enforcer.prototype.enforce = function enforce (expectedPermissions) { return keycloak.checkPermissions(authzRequest, request, function (permissions) { if (handlePermissions(expectedPermissions, function (resource, scope) { if (!permissions || permissions.length === 0) { - return false; + return false } for (let j = 0; j < permissions.length; j++) { - const permission = permissions[j]; + const permission = permissions[j] if (permission.rsid === resource || permission.rsname === resource) { if (scope) { if (permission.scopes && permission.scopes.length > 0) { if (!permission.scopes.includes(scope)) { - return false; + return false } - break; + break } - return false; + return false } } } })) { - request.permissions = permissions; - return next(); + request.permissions = permissions + return next() } - return keycloak.accessDenied(request, response, next); + return keycloak.accessDenied(request, response, next) }).catch(function () { - return keycloak.accessDenied(request, response, next); - }); + return keycloak.accessDenied(request, response, next) + }) } else if (config.response_mode === 'token') { - authzRequest.response_mode = undefined; + authzRequest.response_mode = undefined return keycloak.checkPermissions(authzRequest, request).then(function (grant) { if (handlePermissions(expectedPermissions, function (resource, scope) { if (!grant.access_token.hasPermission(resource, scope)) { - return false; + return false } })) { - return next(); + return next() } - return keycloak.accessDenied(request, response, next); + return keycloak.accessDenied(request, response, next) }).catch(function () { - return keycloak.accessDenied(request, response, next); - }); + return keycloak.accessDenied(request, response, next) + }) } - }; -}; + } +} -module.exports = Enforcer; +module.exports = Enforcer diff --git a/middleware/grant-attacher.js b/middleware/grant-attacher.js index 08b1bf56..88fd06a7 100644 --- a/middleware/grant-attacher.js +++ b/middleware/grant-attacher.js @@ -13,14 +13,14 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' module.exports = function (keycloak) { return function grantAttacher (request, response, next) { keycloak.getGrant(request, response) .then(grant => { - request.kauth.grant = grant; + request.kauth.grant = grant }) - .then(next).catch(() => next()); - }; -}; + .then(next).catch(() => next()) + } +} diff --git a/middleware/logout.js b/middleware/logout.js index 8918e42f..c7855958 100644 --- a/middleware/logout.js +++ b/middleware/logout.js @@ -13,35 +13,35 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const URL = require('url'); +const URL = require('url') module.exports = function (keycloak, logoutUrl) { return function logout (request, response, next) { const parsedRequest = URL.parse(request.url, true); // eslint-disable-line if (parsedRequest.pathname !== logoutUrl) { - return next(); + return next() } - let idTokenHint = null; + let idTokenHint = null if (request.kauth.grant) { - idTokenHint = request.kauth.grant.id_token.token; - keycloak.deauthenticated(request); - request.kauth.grant.unstore(request, response); - delete request.kauth.grant; + idTokenHint = request.kauth.grant.id_token.token + keycloak.deauthenticated(request) + request.kauth.grant.unstore(request, response) + delete request.kauth.grant } - const queryParams = parsedRequest.query; - let redirectUrl = queryParams && queryParams.redirect_url; + const queryParams = parsedRequest.query + let redirectUrl = queryParams && queryParams.redirect_url if (!redirectUrl) { - const host = request.hostname; - const headerHost = request.headers.host.split(':'); - const port = headerHost[1] || ''; - redirectUrl = request.protocol + '://' + host + (port === '' ? '' : ':' + port) + '/'; + const host = request.hostname + const headerHost = request.headers.host.split(':') + const port = headerHost[1] || '' + redirectUrl = request.protocol + '://' + host + (port === '' ? '' : ':' + port) + '/' } - const keycloakLogoutUrl = keycloak.logoutUrl(redirectUrl, idTokenHint); + const keycloakLogoutUrl = keycloak.logoutUrl(redirectUrl, idTokenHint) - response.redirect(keycloakLogoutUrl); - }; -}; + response.redirect(keycloakLogoutUrl) + } +} diff --git a/middleware/post-auth.js b/middleware/post-auth.js index 852aca29..8bd74447 100644 --- a/middleware/post-auth.js +++ b/middleware/post-auth.js @@ -13,23 +13,23 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const URL = require('url'); +const URL = require('url') module.exports = function (keycloak) { return function postAuth (request, response, next) { if (!request.query.auth_callback) { - return next(); + return next() } // During the check SSO process the Keycloak server answered the user is not logged in if (request.query.error === 'login_required') { - return next(); + return next() } if (request.query.error) { - return keycloak.accessDenied(request, response, next); + return keycloak.accessDenied(request, response, next) } keycloak.getGrantFromCode(request.query.code, request, response) @@ -37,25 +37,25 @@ module.exports = function (keycloak) { const urlParts = { pathname: request.path, query: request.query - }; + } - delete urlParts.query.code; - delete urlParts.query.auth_callback; - delete urlParts.query.state; - delete urlParts.query.session_state; + delete urlParts.query.code + delete urlParts.query.auth_callback + delete urlParts.query.state + delete urlParts.query.session_state - const cleanUrl = URL.format(urlParts); + const cleanUrl = URL.format(urlParts) - request.kauth.grant = grant; + request.kauth.grant = grant try { - keycloak.authenticated(request); + keycloak.authenticated(request) } catch (err) { - console.log(err); + console.log(err) } - response.redirect(cleanUrl); + response.redirect(cleanUrl) }).catch((err) => { - keycloak.accessDenied(request, response, next); - console.error('Could not obtain grant code: ' + err); - }); - }; -}; + keycloak.accessDenied(request, response, next) + console.error('Could not obtain grant code: ' + err) + }) + } +} diff --git a/middleware/protect.js b/middleware/protect.js index c2b83d60..e42c747d 100644 --- a/middleware/protect.js +++ b/middleware/protect.js @@ -13,54 +13,54 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const UUID = require('./../uuid'); +const UUID = require('./../uuid') function forceLogin (keycloak, request, response) { - const host = request.hostname; - const headerHost = request.headers.host.split(':'); - const port = headerHost[1] || ''; - const protocol = request.protocol; - const hasQuery = ~(request.originalUrl || request.url).indexOf('?'); + const host = request.hostname + const headerHost = request.headers.host.split(':') + const port = headerHost[1] || '' + const protocol = request.protocol + const hasQuery = ~(request.originalUrl || request.url).indexOf('?') - const redirectUrl = protocol + '://' + host + (port === '' ? '' : ':' + port) + (request.originalUrl || request.url) + (hasQuery ? '&' : '?') + 'auth_callback=1'; + const redirectUrl = protocol + '://' + host + (port === '' ? '' : ':' + port) + (request.originalUrl || request.url) + (hasQuery ? '&' : '?') + 'auth_callback=1' if (request.session) { - request.session.auth_redirect_uri = redirectUrl; + request.session.auth_redirect_uri = redirectUrl } - const uuid = UUID(); - const loginURL = keycloak.loginUrl(uuid, redirectUrl); - response.redirect(loginURL); + const uuid = UUID() + const loginURL = keycloak.loginUrl(uuid, redirectUrl) + response.redirect(loginURL) } function simpleGuard (role, token) { - return token.hasRole(role); + return token.hasRole(role) } module.exports = function (keycloak, spec) { - let guard; + let guard if (typeof spec === 'function') { - guard = spec; + guard = spec } else if (typeof spec === 'string') { - guard = simpleGuard.bind(undefined, spec); + guard = simpleGuard.bind(undefined, spec) } return function protect (request, response, next) { if (request.kauth && request.kauth.grant) { if (!guard || guard(request.kauth.grant.access_token, request, response)) { - return next(); + return next() } - return keycloak.accessDenied(request, response, next); + return keycloak.accessDenied(request, response, next) } if (keycloak.redirectToLogin(request)) { - forceLogin(keycloak, request, response); + forceLogin(keycloak, request, response) } else { - return keycloak.accessDenied(request, response, next); + return keycloak.accessDenied(request, response, next) } - }; -}; + } +} diff --git a/middleware/setup.js b/middleware/setup.js index 39eb74cd..c0665eed 100644 --- a/middleware/setup.js +++ b/middleware/setup.js @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' module.exports = function setup (request, response, next) { - request.kauth = {}; - next(); -}; + request.kauth = {} + next() +} diff --git a/package-lock.json b/package-lock.json index f556ad70..a6436e41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,8 +32,8 @@ "nyc": "^15.0.0", "rsa-compat": "^2.0.8", "selenium-webdriver": "^3.6.0", - "semistandard": "^16.0.1", "server-destroy": "^1.0.1", + "standard": "^17.0.0", "tap-xunit": "^2.4.1", "tape": "^5.4.1", "tape-catch": "^1.0.6", @@ -387,29 +387,35 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -424,9 +430,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -438,13 +444,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">= 4" + "node": "*" } }, "node_modules/@eslint/eslintrc/node_modules/ms": { @@ -466,12 +487,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", "minimatch": "^3.0.4" }, @@ -480,9 +501,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -753,7 +774,7 @@ "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "node_modules/@types/linkify-it": { @@ -813,9 +834,9 @@ } }, "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -897,15 +918,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -961,14 +973,14 @@ "dev": true }, "node_modules/array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", "get-intrinsic": "^1.1.1", "is-string": "^1.0.7" }, @@ -1007,14 +1019,15 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", - "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -1024,14 +1037,15 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", - "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -1069,15 +1083,6 @@ "node": ">=0.8" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1433,6 +1438,30 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/builtins": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-4.1.0.tgz", + "integrity": "sha512-1bPRZQtmKaO6h7qV1YHXNtr6nCK28k0Zo95KM4dXfILcZZwoHJBN1m3lfLv9LPkcOZlrSr+J1bzMaZFO98Yq0w==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1879,15 +1908,19 @@ } }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/defined": { @@ -2173,18 +2206,6 @@ "once": "^1.4.0" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/entities": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", @@ -2201,31 +2222,34 @@ } }, "node_modules/es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", "get-intrinsic": "^1.1.1", "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.2", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2259,6 +2283,15 @@ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -2307,49 +2340,44 @@ } }, "node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", + "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", "dev": true, "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -2357,29 +2385,16 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-config-semistandard": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-semistandard/-/eslint-config-semistandard-16.0.0.tgz", - "integrity": "sha512-oD8QOo4mSInRJhQb3Zi6L8HebwZaB6SI3A+NNrPdVN0nN1K45L5pXK3joY+ksWDlT3ew/M+fJk2tuMCjIpjRzQ==", - "dev": true, - "peerDependencies": { - "eslint": ">=7.12.1", - "eslint-config-standard": ">=16.0.3", - "eslint-plugin-import": ">=2.22.1", - "eslint-plugin-node": ">=11.1.0", - "eslint-plugin-promise": ">=4.2.1" - } - }, "node_modules/eslint-config-standard": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", - "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz", + "integrity": "sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==", "dev": true, "funding": [ { @@ -2396,16 +2411,16 @@ } ], "peerDependencies": { - "eslint": "^7.12.1", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1 || ^5.0.0" + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0" } }, "node_modules/eslint-config-standard-jsx": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz", - "integrity": "sha512-hLeA2f5e06W1xyr/93/QJulN/rLbUVUmqTlexv9PRKHFwEC9ffJcH2LvJhMoEqYQBEYafedgGZXH2W8NUpt5lA==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-11.0.0.tgz", + "integrity": "sha512-+1EV/R0JxEK1L0NGolAr8Iktm3Rgotx3BKwgaX+eAuSX8D952LULKtjgZD3F+e6SvibONnhLwoTi9DPxN5LvvQ==", "dev": true, "funding": [ { @@ -2422,8 +2437,8 @@ } ], "peerDependencies": { - "eslint": "^7.12.1", - "eslint-plugin-react": "^7.21.5" + "eslint": "^8.8.0", + "eslint-plugin-react": "^7.28.0" } }, "node_modules/eslint-import-resolver-node": { @@ -2452,9 +2467,9 @@ "dev": true }, "node_modules/eslint-module-utils": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz", - "integrity": "sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", "dev": true, "dependencies": { "debug": "^3.2.7", @@ -2547,9 +2562,9 @@ } }, "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", "dev": true, "dependencies": { "eslint-utils": "^2.0.0", @@ -2565,10 +2580,34 @@ "eslint": ">=4.19.1" } }, + "node_modules/eslint-plugin-es/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "dev": true, "dependencies": { "array-includes": "^3.1.4", @@ -2576,14 +2615,14 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", + "eslint-module-utils": "^2.7.3", "has": "^1.0.3", - "is-core-module": "^2.8.0", + "is-core-module": "^2.8.1", "is-glob": "^4.0.3", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "object.values": "^1.1.5", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" }, "engines": { "node": ">=4" @@ -2604,61 +2643,98 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-n": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.2.0.tgz", + "integrity": "sha512-lWLg++jGwC88GDGGBX3CMkk0GIWq0y41aH51lavWApOKcMQcYoL3Ayd0lEdtD3SnQtR+3qBvWQS3qGbR2BxRWg==", + "dev": true, + "dependencies": { + "builtins": "^4.0.0", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", "ignore": "^5.1.1", + "is-core-module": "^2.3.0", "minimatch": "^3.0.4", "resolve": "^1.10.1", - "semver": "^6.1.0" + "semver": "^6.3.0" }, "engines": { - "node": ">=8.10.0" + "node": ">=12.22.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" }, "peerDependencies": { - "eslint": ">=5.16.0" + "eslint": ">=7.0.0" } }, "node_modules/eslint-plugin-promise": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.2.0.tgz", - "integrity": "sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz", + "integrity": "sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==", "dev": true, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { - "eslint": "^7.0.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/eslint-plugin-react": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz", - "integrity": "sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g==", + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.0.tgz", + "integrity": "sha512-RgwH7hjW48BleKsYyHK5vUAvxtE9SMPDKmcPRQgtRCYaZA0XQPt5FSkrU3nhz5ifzMZcA8opwmRJ2cmOO8tr5A==", "dev": true, "dependencies": { - "array-includes": "^3.1.1", - "array.prototype.flatmap": "^1.2.3", + "array-includes": "^3.1.5", + "array.prototype.flatmap": "^1.3.0", "doctrine": "^2.1.0", - "has": "^1.0.3", + "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "object.entries": "^1.1.2", - "object.fromentries": "^2.0.2", - "object.values": "^1.1.1", - "prop-types": "^15.7.2", - "resolve": "^1.18.1", - "string.prototype.matchall": "^4.0.2" + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.1", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.7" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -2673,44 +2749,63 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^2.0.0" }, "engines": { - "node": ">=6" + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" } }, "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", @@ -2719,13 +2814,13 @@ "node": ">=10" } }, - "node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/eslint/node_modules/ansi-styles": { @@ -2743,6 +2838,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2778,9 +2879,9 @@ "dev": true }, "node_modules/eslint/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -2806,10 +2907,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/eslint/node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -2830,13 +2943,28 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">= 4" + "node": "*" } }, "node_modules/eslint/node_modules/ms": { @@ -2845,21 +2973,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/eslint/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2885,26 +2998,17 @@ } }, "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", "dev": true, "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/esprima": { @@ -2932,15 +3036,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -2953,7 +3048,7 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -2962,15 +3057,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3311,9 +3397,9 @@ } }, "node_modules/flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "node_modules/follow-redirects": { @@ -3455,12 +3541,39 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3578,7 +3691,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, + "optional": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -3684,9 +3797,9 @@ } }, "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3714,10 +3827,22 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, "engines": { "node": ">= 0.4" @@ -4094,9 +4219,9 @@ } }, "node_modules/is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -4263,10 +4388,13 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4763,12 +4891,12 @@ } }, "node_modules/jsx-ast-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", - "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.0.tgz", + "integrity": "sha512-XzO9luP6L0xkxwhIJMTJQpZo/eeN60K08jHdexfD569AGxeNug6UketeHXEhROoM8aR7EcUoOQmIhcJQjcuq8Q==", "dev": true, "dependencies": { - "array-includes": "^3.1.3", + "array-includes": "^3.1.4", "object.assign": "^4.1.2" }, "engines": { @@ -5002,12 +5130,6 @@ "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", "dev": true }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, "node_modules/log-driver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", @@ -5539,6 +5661,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.hasown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", + "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object.values": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", @@ -5961,15 +6096,6 @@ "node": ">=8" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -6169,13 +6295,14 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -6258,15 +6385,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -6551,43 +6669,6 @@ "rimraf": "bin.js" } }, - "node_modules/semistandard": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/semistandard/-/semistandard-16.0.1.tgz", - "integrity": "sha512-ApAJ9fMAIwYuk5xI2HWSCd8s5o5L95abxU4dYl6ovUX6Rcww/7oxtaSuu9wLFL/Gfj/EXx1h6S4itXy5vyL60Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "eslint": "^7.27.0", - "eslint-config-semistandard": "16.0.0", - "eslint-config-standard": "16.0.3", - "eslint-config-standard-jsx": "10.0.0", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^5.1.0", - "eslint-plugin-react": "~7.21.5", - "standard-engine": "^14.0.0" - }, - "bin": { - "semistandard": "bin/cmd.js" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -6719,56 +6800,6 @@ "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -6835,10 +6866,46 @@ "node": ">=0.10.0" } }, + "node_modules/standard": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/standard/-/standard-17.0.0.tgz", + "integrity": "sha512-GlCM9nzbLUkr+TYR5I2WQoIah4wHA2lMauqbyPLV/oI5gJxqhHzhjl9EG2N0lr/nRqI3KCbCvm/W3smxvLaChA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "eslint": "^8.13.0", + "eslint-config-standard": "17.0.0", + "eslint-config-standard-jsx": "^11.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-n": "^15.1.0", + "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-react": "^7.28.0", + "standard-engine": "^15.0.0" + }, + "bin": { + "standard": "bin/cmd.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/standard-engine": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz", - "integrity": "sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-15.0.0.tgz", + "integrity": "sha512-4xwUhJNo1g/L2cleysUqUv7/btn7GEbYJvmgKrQ2vd/8pkTmN8cpqAZg+BT8Z1hNeEH787iWUdOpL8fmApLtxA==", "dev": true, "funding": [ { @@ -6856,12 +6923,12 @@ ], "dependencies": { "get-stdin": "^8.0.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "pkg-conf": "^3.1.0", "xdg-basedir": "^4.0.0" }, "engines": { - "node": ">=8.10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/statuses": { @@ -6909,18 +6976,18 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "regexp.prototype.flags": "^1.4.1", "side-channel": "^1.0.4" }, "funding": { @@ -6945,26 +7012,28 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7015,44 +7084,18 @@ "node": ">=4" } }, - "node_modules/table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "node": ">= 0.4" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", @@ -7409,14 +7452,14 @@ "dev": true }, "node_modules/tsconfig-paths": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", - "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.1", - "minimist": "^1.2.0", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, @@ -7535,14 +7578,14 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" }, "funding": { @@ -8195,45 +8238,63 @@ } }, "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "requires": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" } }, "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } }, "ms": { "version": "2.1.2", @@ -8250,20 +8311,20 @@ } }, "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", "minimatch": "^3.0.4" }, "dependencies": { "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -8498,7 +8559,7 @@ "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "@types/linkify-it": { @@ -8555,9 +8616,9 @@ } }, "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true }, "acorn-jsx": { @@ -8615,12 +8676,6 @@ "uri-js": "^4.2.2" } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -8667,14 +8722,14 @@ "dev": true }, "array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", "get-intrinsic": "^1.1.1", "is-string": "^1.0.7" } @@ -8698,25 +8753,27 @@ } }, "array.prototype.flat": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", - "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" } }, "array.prototype.flatmap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", - "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" } }, "asn1": { @@ -8745,12 +8802,6 @@ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -9033,6 +9084,26 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "builtins": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-4.1.0.tgz", + "integrity": "sha512-1bPRZQtmKaO6h7qV1YHXNtr6nCK28k0Zo95KM4dXfILcZZwoHJBN1m3lfLv9LPkcOZlrSr+J1bzMaZFO98Yq0w==", + "dev": true, + "requires": { + "semver": "^7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -9401,12 +9472,13 @@ } }, "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "defined": { @@ -9656,15 +9728,6 @@ "once": "^1.4.0" } }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "entities": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", @@ -9681,31 +9744,34 @@ } }, "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", "get-intrinsic": "^1.1.1", "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.2", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" } }, "es-get-iterator": { @@ -9732,6 +9798,15 @@ } } }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -9768,62 +9843,48 @@ "dev": true }, "eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", + "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", "dev": true, "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -9833,6 +9894,12 @@ "color-convert": "^2.0.1" } }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -9859,9 +9926,9 @@ "dev": true }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -9873,10 +9940,19 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -9888,11 +9964,23 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } }, "ms": { "version": "2.1.2", @@ -9900,15 +9988,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -9926,24 +10005,17 @@ } } }, - "eslint-config-semistandard": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-semistandard/-/eslint-config-semistandard-16.0.0.tgz", - "integrity": "sha512-oD8QOo4mSInRJhQb3Zi6L8HebwZaB6SI3A+NNrPdVN0nN1K45L5pXK3joY+ksWDlT3ew/M+fJk2tuMCjIpjRzQ==", - "dev": true, - "requires": {} - }, "eslint-config-standard": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", - "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz", + "integrity": "sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==", "dev": true, "requires": {} }, "eslint-config-standard-jsx": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz", - "integrity": "sha512-hLeA2f5e06W1xyr/93/QJulN/rLbUVUmqTlexv9PRKHFwEC9ffJcH2LvJhMoEqYQBEYafedgGZXH2W8NUpt5lA==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-11.0.0.tgz", + "integrity": "sha512-+1EV/R0JxEK1L0NGolAr8Iktm3Rgotx3BKwgaX+eAuSX8D952LULKtjgZD3F+e6SvibONnhLwoTi9DPxN5LvvQ==", "dev": true, "requires": {} }, @@ -9975,9 +10047,9 @@ } }, "eslint-module-utils": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz", - "integrity": "sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", "dev": true, "requires": { "debug": "^3.2.7", @@ -10051,19 +10123,36 @@ } }, "eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", "dev": true, "requires": { "eslint-utils": "^2.0.0", "regexpp": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "dev": true, "requires": { "array-includes": "^3.1.4", @@ -10071,14 +10160,14 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", + "eslint-module-utils": "^2.7.3", "has": "^1.0.3", - "is-core-module": "^2.8.0", + "is-core-module": "^2.8.1", "is-glob": "^4.0.3", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "object.values": "^1.1.5", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" }, "dependencies": { "doctrine": { @@ -10089,47 +10178,72 @@ "requires": { "esutils": "^2.0.2" } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } } } }, - "eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "eslint-plugin-n": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.2.0.tgz", + "integrity": "sha512-lWLg++jGwC88GDGGBX3CMkk0GIWq0y41aH51lavWApOKcMQcYoL3Ayd0lEdtD3SnQtR+3qBvWQS3qGbR2BxRWg==", "dev": true, "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", + "builtins": "^4.0.0", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", "ignore": "^5.1.1", + "is-core-module": "^2.3.0", "minimatch": "^3.0.4", "resolve": "^1.10.1", - "semver": "^6.1.0" + "semver": "^6.3.0" } }, "eslint-plugin-promise": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.2.0.tgz", - "integrity": "sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz", + "integrity": "sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==", "dev": true, "requires": {} }, "eslint-plugin-react": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz", - "integrity": "sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g==", + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.0.tgz", + "integrity": "sha512-RgwH7hjW48BleKsYyHK5vUAvxtE9SMPDKmcPRQgtRCYaZA0XQPt5FSkrU3nhz5ifzMZcA8opwmRJ2cmOO8tr5A==", "dev": true, "requires": { - "array-includes": "^3.1.1", - "array.prototype.flatmap": "^1.2.3", + "array-includes": "^3.1.5", + "array.prototype.flatmap": "^1.3.0", "doctrine": "^2.1.0", - "has": "^1.0.3", + "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "object.entries": "^1.1.2", - "object.fromentries": "^2.0.2", - "object.values": "^1.1.1", - "prop-types": "^15.7.2", - "resolve": "^1.18.1", - "string.prototype.matchall": "^4.0.2" + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.1", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.7" }, "dependencies": { "doctrine": { @@ -10140,59 +10254,70 @@ "requires": { "esutils": "^2.0.2" } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } } } }, "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "requires": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" } }, "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^2.0.0" }, "dependencies": { "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true } } }, "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", "dev": true, "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" } }, "esprima": { @@ -10208,14 +10333,6 @@ "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "esrecurse": { @@ -10225,20 +10342,12 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { @@ -10507,9 +10616,9 @@ } }, "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "follow-redirects": { @@ -10605,12 +10714,30 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -10692,7 +10819,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, + "optional": true, "requires": { "is-glob": "^4.0.1" } @@ -10773,9 +10900,9 @@ } }, "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true }, "has-dynamic-import": { @@ -10794,10 +10921,19 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, "has-tostringtag": { @@ -11074,9 +11210,9 @@ "dev": true }, "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "requires": { "has": "^1.0.3" @@ -11186,10 +11322,13 @@ "dev": true }, "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } }, "is-stream": { "version": "2.0.1", @@ -11574,12 +11713,12 @@ } }, "jsx-ast-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", - "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.0.tgz", + "integrity": "sha512-XzO9luP6L0xkxwhIJMTJQpZo/eeN60K08jHdexfD569AGxeNug6UketeHXEhROoM8aR7EcUoOQmIhcJQjcuq8Q==", "dev": true, "requires": { - "array-includes": "^3.1.3", + "array-includes": "^3.1.4", "object.assign": "^4.1.2" } }, @@ -11787,12 +11926,6 @@ "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", "dev": true }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, "log-driver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", @@ -12181,6 +12314,16 @@ "es-abstract": "^1.19.1" } }, + "object.hasown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", + "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", + "dev": true, + "requires": { + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, "object.values": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", @@ -12501,12 +12644,6 @@ "fromentries": "^1.2.0" } }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -12661,13 +12798,14 @@ } }, "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" } }, "regexpp": { @@ -12727,12 +12865,6 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -12936,23 +13068,6 @@ } } }, - "semistandard": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/semistandard/-/semistandard-16.0.1.tgz", - "integrity": "sha512-ApAJ9fMAIwYuk5xI2HWSCd8s5o5L95abxU4dYl6ovUX6Rcww/7oxtaSuu9wLFL/Gfj/EXx1h6S4itXy5vyL60Q==", - "dev": true, - "requires": { - "eslint": "^7.27.0", - "eslint-config-semistandard": "16.0.0", - "eslint-config-standard": "16.0.3", - "eslint-config-standard-jsx": "10.0.0", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^5.1.0", - "eslint-plugin-react": "~7.21.5", - "standard-engine": "^14.0.0" - } - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -13062,43 +13177,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "optional": true }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -13148,14 +13226,30 @@ "tweetnacl": "~0.14.0" } }, + "standard": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/standard/-/standard-17.0.0.tgz", + "integrity": "sha512-GlCM9nzbLUkr+TYR5I2WQoIah4wHA2lMauqbyPLV/oI5gJxqhHzhjl9EG2N0lr/nRqI3KCbCvm/W3smxvLaChA==", + "dev": true, + "requires": { + "eslint": "^8.13.0", + "eslint-config-standard": "17.0.0", + "eslint-config-standard-jsx": "^11.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-n": "^15.1.0", + "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-react": "^7.28.0", + "standard-engine": "^15.0.0" + } + }, "standard-engine": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz", - "integrity": "sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-15.0.0.tgz", + "integrity": "sha512-4xwUhJNo1g/L2cleysUqUv7/btn7GEbYJvmgKrQ2vd/8pkTmN8cpqAZg+BT8Z1hNeEH787iWUdOpL8fmApLtxA==", "dev": true, "requires": { "get-stdin": "^8.0.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "pkg-conf": "^3.1.0", "xdg-basedir": "^4.0.0" } @@ -13196,18 +13290,18 @@ } }, "string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "regexp.prototype.flags": "^1.4.1", "side-channel": "^1.0.4" } }, @@ -13223,23 +13317,25 @@ } }, "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "strip-ansi": { @@ -13272,38 +13368,11 @@ "has-flag": "^3.0.0" } }, - "table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true }, "taffydb": { "version": "2.6.2", @@ -13621,14 +13690,14 @@ "dev": true }, "tsconfig-paths": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", - "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "requires": { "@types/json5": "^0.0.29", "json5": "^1.0.1", - "minimist": "^1.2.0", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" }, "dependencies": { @@ -13721,14 +13790,14 @@ } }, "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" } }, diff --git a/package.json b/package.json index 28086bc2..ae83d74b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "homepage": "http://keycloak.org", "main": "keycloak.js", "scripts": { - "lint": "jshint *.js stores/*.js middleware/*.js middleware/auth-utils/*.js example/*.js && semistandard", + "lint": "jshint *.js stores/*.js middleware/*.js middleware/auth-utils/*.js example/*.js && standard", "test": "./run-tests.sh", "docs": "jsdoc --verbose -d docs -t ./node_modules/ink-docstrap/template -R README.md index.js ./middleware/*.js stores/*.js ./middleware/auth-utils/*.js", "coverage": "nyc cover tape test/unit/*.js tape test/*.js", @@ -59,8 +59,8 @@ "nyc": "^15.0.0", "rsa-compat": "^2.0.8", "selenium-webdriver": "^3.6.0", - "semistandard": "^16.0.1", "server-destroy": "^1.0.1", + "standard": "^17.0.0", "tap-xunit": "^2.4.1", "tape": "^5.4.1", "tape-catch": "^1.0.6", @@ -69,7 +69,7 @@ "optionalDependencies": { "chromedriver": "latest" }, - "semistandard": { + "standard": { "ignore": [ "**/keycloak*/" ] diff --git a/scripts/start-server.mjs b/scripts/start-server.mjs index 5af19ef0..9d5c2fea 100755 --- a/scripts/start-server.mjs +++ b/scripts/start-server.mjs @@ -1,82 +1,82 @@ #!/usr/bin/env node // @ts-check -import { Octokit } from "@octokit/rest"; -import gunzip from "gunzip-maybe"; -import fetch from "node-fetch"; -import { spawn } from "node:child_process"; -import fs from "node:fs"; -import path from "node:path"; -import { pipeline } from "node:stream/promises"; -import { fileURLToPath } from "node:url"; -import tar from "tar-fs"; +import { Octokit } from '@octokit/rest' +import gunzip from 'gunzip-maybe' +import fetch from 'node-fetch' +import { spawn } from 'node:child_process' +import fs from 'node:fs' +import path from 'node:path' +import { pipeline } from 'node:stream/promises' +import { fileURLToPath } from 'node:url' +import tar from 'tar-fs' -const DIR_NAME = path.dirname(fileURLToPath(import.meta.url)); -const SERVER_DIR = path.resolve(DIR_NAME, "../server"); -const SCRIPT_EXTENSION = process.platform === "win32" ? ".bat" : ".sh"; +const DIR_NAME = path.dirname(fileURLToPath(import.meta.url)) +const SERVER_DIR = path.resolve(DIR_NAME, '../server') +const SCRIPT_EXTENSION = process.platform === 'win32' ? '.bat' : '.sh' -await startServer(); +await startServer() -async function startServer() { - await downloadServer(); +async function startServer () { + await downloadServer() - console.info("Starting server…"); + console.info('Starting server…') - const args = process.argv.slice(2); + const args = process.argv.slice(2) const child = spawn( path.join(SERVER_DIR, `bin/kc${SCRIPT_EXTENSION}`), - ["start-dev", ...args], + ['start-dev', ...args], { env: { - KEYCLOAK_ADMIN: "admin", - KEYCLOAK_ADMIN_PASSWORD: "admin", - ...process.env, - }, + KEYCLOAK_ADMIN: 'admin', + KEYCLOAK_ADMIN_PASSWORD: 'admin', + ...process.env + } } - ); + ) - child.stdout.pipe(process.stdout); - child.stderr.pipe(process.stderr); + child.stdout.pipe(process.stdout) + child.stderr.pipe(process.stderr) } -async function downloadServer() { - const directoryExists = fs.existsSync(SERVER_DIR); +async function downloadServer () { + const directoryExists = fs.existsSync(SERVER_DIR) if (directoryExists) { - console.info("Server installation found, skipping download."); - return; + console.info('Server installation found, skipping download.') + return } - console.info("Downloading and extracting server…"); + console.info('Downloading and extracting server…') - const nightlyAsset = await getNightlyAsset(); - const assetStream = await getAssetAsStream(nightlyAsset); + const nightlyAsset = await getNightlyAsset() + const assetStream = await getAssetAsStream(nightlyAsset) - await extractTarball(assetStream, SERVER_DIR, { strip: 1 }); + await extractTarball(assetStream, SERVER_DIR, { strip: 1 }) } -async function getNightlyAsset() { - const api = new Octokit(); +async function getNightlyAsset () { + const api = new Octokit() const release = await api.repos.getReleaseByTag({ - owner: "keycloak", - repo: "keycloak", - tag: "nightly", - }); + owner: 'keycloak', + repo: 'keycloak', + tag: 'nightly' + }) return release.data.assets.find( - ({ name }) => name === "keycloak-999-SNAPSHOT.tar.gz" - ); + ({ name }) => name === 'keycloak-999-SNAPSHOT.tar.gz' + ) } -async function getAssetAsStream(asset) { - const response = await fetch(asset.browser_download_url); +async function getAssetAsStream (asset) { + const response = await fetch(asset.browser_download_url) if (!response.ok) { - throw new Error("Something went wrong requesting the nightly release."); + throw new Error('Something went wrong requesting the nightly release.') } - return response.body; + return response.body } -function extractTarball(stream, path, options) { - return pipeline(stream, gunzip(), tar.extract(path, options)); +function extractTarball (stream, path, options) { + return pipeline(stream, gunzip(), tar.extract(path, options)) } diff --git a/stores/bearer-store.js b/stores/bearer-store.js index b579bcad..2fb35712 100644 --- a/stores/bearer-store.js +++ b/stores/bearer-store.js @@ -13,21 +13,21 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const BearerStore = {}; +const BearerStore = {} BearerStore.get = (request) => { - const header = request.headers.authorization; + const header = request.headers.authorization if (header) { if (header.indexOf('bearer ') === 0 || header.indexOf('Bearer ') === 0) { - const accessToken = header.substring(7); + const accessToken = header.substring(7) return { access_token: accessToken - }; + } } } -}; +} -module.exports = BearerStore; +module.exports = BearerStore diff --git a/stores/cookie-store.js b/stores/cookie-store.js index 5e2bf208..6f05f918 100644 --- a/stores/cookie-store.js +++ b/stores/cookie-store.js @@ -13,36 +13,36 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const CookieStore = {}; +const CookieStore = {} -CookieStore.TOKEN_KEY = 'keycloak-token'; +CookieStore.TOKEN_KEY = 'keycloak-token' CookieStore.get = (request) => { - const value = request.cookies[CookieStore.TOKEN_KEY]; + const value = request.cookies[CookieStore.TOKEN_KEY] if (value) { try { - return JSON.parse(value); + return JSON.parse(value) } catch (err) { // ignore } } -}; +} const store = (grant) => { return (request, response) => { - response.cookie(CookieStore.TOKEN_KEY, grant.__raw); - }; -}; + response.cookie(CookieStore.TOKEN_KEY, grant.__raw) + } +} const unstore = (request, response) => { - response.clearCookie(CookieStore.TOKEN_KEY); -}; + response.clearCookie(CookieStore.TOKEN_KEY) +} CookieStore.wrap = (grant) => { - grant.store = store(grant); - grant.unstore = unstore; -}; + grant.store = store(grant) + grant.unstore = unstore +} -module.exports = CookieStore; +module.exports = CookieStore diff --git a/stores/session-store.js b/stores/session-store.js index 36d0b573..baf54abc 100644 --- a/stores/session-store.js +++ b/stores/session-store.js @@ -13,44 +13,44 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' function SessionStore (store) { - this.store = store; + this.store = store } -SessionStore.TOKEN_KEY = 'keycloak-token'; +SessionStore.TOKEN_KEY = 'keycloak-token' -SessionStore.prototype.get = (request) => request.session[SessionStore.TOKEN_KEY]; +SessionStore.prototype.get = (request) => request.session[SessionStore.TOKEN_KEY] SessionStore.prototype.clear = function (sessionId) { - const self = this; + const self = this this.store.get(sessionId, (err, session) => { if (err) { - console.log(err); + console.log(err) } if (session) { - delete session[SessionStore.TOKEN_KEY]; - self.store.set(sessionId, session); + delete session[SessionStore.TOKEN_KEY] + self.store.set(sessionId, session) } - }); -}; + }) +} const store = (grant) => { return (request, response) => { - request.session[SessionStore.TOKEN_KEY] = grant.__raw; - }; -}; + request.session[SessionStore.TOKEN_KEY] = grant.__raw + } +} const unstore = (request, response) => { - delete request.session[SessionStore.TOKEN_KEY]; -}; + delete request.session[SessionStore.TOKEN_KEY] +} SessionStore.prototype.wrap = (grant) => { if (grant) { - grant.store = store(grant); - grant.unstore = unstore; + grant.store = store(grant) + grant.unstore = unstore } -}; +} -module.exports = SessionStore; +module.exports = SessionStore diff --git a/test/fixtures/node-console/index.js b/test/fixtures/node-console/index.js index 07ef9042..f74c5a5c 100644 --- a/test/fixtures/node-console/index.js +++ b/test/fixtures/node-console/index.js @@ -13,92 +13,92 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const Keycloak = require('../../../'); -const bodyParser = require('body-parser'); -const hogan = require('hogan-express'); -const express = require('express'); -const session = require('express-session'); -const cookieParser = require('cookie-parser'); -const enableDestroy = require('server-destroy'); -const parseClient = require('../../utils/helper').parseClient; +const Keycloak = require('../../../') +const bodyParser = require('body-parser') +const hogan = require('hogan-express') +const express = require('express') +const session = require('express-session') +const cookieParser = require('cookie-parser') +const enableDestroy = require('server-destroy') +const parseClient = require('../../utils/helper').parseClient Keycloak.prototype.redirectToLogin = function (req) { - const apiMatcher = /^\/service\/.*/i; - return !apiMatcher.test(req.baseUrl); -}; + const apiMatcher = /^\/service\/.*/i + return !apiMatcher.test(req.baseUrl) +} Keycloak.prototype.obtainDirectly = function (user, pass) { - return this.grantManager.obtainDirectly(user, pass); -}; + return this.grantManager.obtainDirectly(user, pass) +} function NodeApp () { - const app = express(); - app.use(cookieParser()); - const server = app.listen(0); - enableDestroy(server); + const app = express() + app.use(cookieParser()) + const server = app.listen(0) + enableDestroy(server) this.close = function () { - server.close(); - }; + server.close() + } this.destroy = function () { - server.destroy(); - }; - this.port = server.address().port; - this.address = 'http://127.0.0.1:' + this.port; + server.destroy() + } + this.port = server.address().port + this.address = 'http://127.0.0.1:' + this.port - console.log('Testing app listening at http://localhost:%s', this.port); + console.log('Testing app listening at http://localhost:%s', this.port) this.publicClient = function (app) { - const name = app || 'public-app'; - return parseClient('test/fixtures/templates/public-template.json', this.port, name); - }; + const name = app || 'public-app' + return parseClient('test/fixtures/templates/public-template.json', this.port, name) + } this.bearerOnly = function (app) { - const name = app || 'bearer-app'; - return parseClient('test/fixtures/templates/bearerOnly-template.json', this.port, name); - }; + const name = app || 'bearer-app' + return parseClient('test/fixtures/templates/bearerOnly-template.json', this.port, name) + } this.confidential = function (app) { - const name = app || 'confidential-app'; - return parseClient('test/fixtures/templates/confidential-template.json', this.port, name); - }; + const name = app || 'confidential-app' + return parseClient('test/fixtures/templates/confidential-template.json', this.port, name) + } this.enforcerResourceServer = function (app) { - const name = app || 'resource-server-app'; - return parseClient('test/fixtures/templates/resource-server-template.json', this.port, name); - }; + const name = app || 'resource-server-app' + return parseClient('test/fixtures/templates/resource-server-template.json', this.port, name) + } this.build = function (kcConfig, params) { - app.set('view engine', 'html'); - app.set('views', require('path').join(__dirname, '/views')); - app.engine('html', hogan); + app.set('view engine', 'html') + app.set('views', require('path').join(__dirname, '/views')) + app.engine('html', hogan) // Create a session-store to be used by both the express-session // middleware and the keycloak middleware. - const memoryStore = new session.MemoryStore(); + const memoryStore = new session.MemoryStore() app.use(session({ secret: 'mySecret', resave: false, saveUninitialized: true, store: memoryStore - })); + })) // Provide the session store to the Keycloak so that sessions // can be invalidated from the Keycloak console callback. // // Additional configuration is read from keycloak.json file // installed from the Keycloak web console. - params = params || { store: memoryStore }; - const keycloak = new Keycloak(params, kcConfig); + params = params || { store: memoryStore } + const keycloak = new Keycloak(params, kcConfig) // A normal un-protected public URL. app.get('/', function (req, res) { - const authenticated = 'Init Success (' + (req.session['keycloak-token'] ? 'Authenticated' : 'Not Authenticated') + ')'; - output(res, authenticated); - }); + const authenticated = 'Init Success (' + (req.session['keycloak-token'] ? 'Authenticated' : 'Not Authenticated') + ')' + output(res, authenticated) + }) // Install the Keycloak middleware. // @@ -112,108 +112,108 @@ function NodeApp () { app.use(keycloak.middleware({ logout: '/logout', admin: '/' - })); + })) app.get('/login', keycloak.protect(), function (req, res) { - output(res, JSON.stringify(JSON.parse(req.session['keycloak-token']), null, 4), 'Auth Success'); - }); + output(res, JSON.stringify(JSON.parse(req.session['keycloak-token']), null, 4), 'Auth Success') + }) app.get('/check-sso', keycloak.checkSso(), function (req, res) { - const authenticated = 'Check SSO Success (' + (req.session['keycloak-token'] ? 'Authenticated' : 'Not Authenticated') + ')'; - output(res, authenticated); - }); + const authenticated = 'Check SSO Success (' + (req.session['keycloak-token'] ? 'Authenticated' : 'Not Authenticated') + ')' + output(res, authenticated) + }) app.get('/restricted', keycloak.protect('realm:admin'), function (req, res) { - const user = req.kauth.grant.access_token.content.preferred_username; - output(res, user, 'Restricted access'); - }); + const user = req.kauth.grant.access_token.content.preferred_username + output(res, user, 'Restricted access') + }) app.get('/cookie', keycloak.protect(), function (req, res) { - const authenticated = req.cookies['keycloak-token'] ? 'Auth Success' : 'Auth Failed'; - output(res, JSON.stringify(JSON.parse(req.cookies['keycloak-token']), null, 4), authenticated); - }); + const authenticated = req.cookies['keycloak-token'] ? 'Auth Success' : 'Auth Failed' + output(res, JSON.stringify(JSON.parse(req.cookies['keycloak-token']), null, 4), authenticated) + }) app.get('/service/public', function (req, res) { - res.json({ message: 'public' }); - }); + res.json({ message: 'public' }) + }) app.get('/service/secured', keycloak.protect('realm:user'), function (req, res) { - res.json({ message: 'secured' }); - }); + res.json({ message: 'secured' }) + }) app.get('/service/admin', keycloak.protect('realm:admin'), function (req, res) { - res.json({ message: 'admin' }); - }); + res.json({ message: 'admin' }) + }) app.get('/service/grant', keycloak.protect(), (req, res, next) => { keycloak.getGrant(req, res) .then(grant => { - res.json(grant); + res.json(grant) }) - .catch(next); - }); + .catch(next) + }) app.post('/service/grant', bodyParser.json(), (req, res, next) => { if (!req.body.username || !req.body.password) { - res.status(400).send('Username and password required'); + res.status(400).send('Username and password required') } keycloak.obtainDirectly(req.body.username, req.body.password) .then(grant => { - keycloak.storeGrant(grant, req, res); - res.json(grant); + keycloak.storeGrant(grant, req, res) + res.json(grant) }) - .catch(next); - }); + .catch(next) + }) app.get('/protected/enforcer/resource', keycloak.enforcer('resource:view'), function (req, res) { - res.json({ message: 'resource:view', permissions: req.permissions }); - }); + res.json({ message: 'resource:view', permissions: req.permissions }) + }) app.post('/protected/enforcer/resource', keycloak.enforcer('resource:update'), function (req, res) { - res.json({ message: 'resource:update', permissions: req.permissions }); - }); + res.json({ message: 'resource:update', permissions: req.permissions }) + }) app.delete('/protected/enforcer/resource', keycloak.enforcer('resource:delete'), function (req, res) { - res.json({ message: 'resource:delete', permissions: req.permissions }); - }); + res.json({ message: 'resource:delete', permissions: req.permissions }) + }) app.get('/protected/enforcer/resource-view-delete', keycloak.enforcer(['resource:view', 'resource:delete']), function (req, res) { - res.json({ message: 'resource:delete', permissions: req.permissions }); - }); + res.json({ message: 'resource:delete', permissions: req.permissions }) + }) app.get('/protected/enforcer/resource-claims', keycloak.enforcer(['photo'], { claims: function (request) { return { user_agent: [request.query.user_agent] - }; + } } }), function (req, res) { - res.json({ message: req.query.user_agent, permissions: req.permissions }); - }); + res.json({ message: req.query.user_agent, permissions: req.permissions }) + }) app.get('/protected/enforcer/no-permission-defined', keycloak.enforcer(), function (req, res) { - res.json({ message: 'always grant', permissions: req.permissions }); - }); + res.json({ message: 'always grant', permissions: req.permissions }) + }) app.get('/protected/web/resource', keycloak.enforcer(['resource:view']), function (req, res) { - const user = req.kauth.grant.access_token.content.preferred_username; - output(res, user, 'Granted'); - }); + const user = req.kauth.grant.access_token.content.preferred_username + output(res, user, 'Granted') + }) app.use('*', function (req, res) { - res.send('Not found!'); - }); - }; + res.send('Not found!') + }) + } } function output (res, output, eventMessage, page) { - page = page || 'index'; + page = page || 'index' res.render(page, { result: output, event: eventMessage - }); + }) } module.exports = { - NodeApp: NodeApp -}; + NodeApp +} diff --git a/test/grant-manager-spec.js b/test/grant-manager-spec.js index 4caaba67..37cd13de 100644 --- a/test/grant-manager-spec.js +++ b/test/grant-manager-spec.js @@ -1,662 +1,662 @@ -'use strict'; +'use strict' -const GrantManager = require('../middleware/auth-utils/grant-manager'); -const Config = require('../middleware/auth-utils/config'); -const test = require('tape'); -const nock = require('nock'); -const delay = (ms) => (value) => new Promise((resolve) => setTimeout(() => resolve(value), ms)); -const getManager = (fixture) => new GrantManager(new Config(fixture)); -const helper = require('./utils/helper'); +const GrantManager = require('../middleware/auth-utils/grant-manager') +const Config = require('../middleware/auth-utils/config') +const test = require('tape') +const nock = require('nock') +const delay = (ms) => (value) => new Promise((resolve) => setTimeout(() => resolve(value), ms)) +const getManager = (fixture) => new GrantManager(new Config(fixture)) +const helper = require('./utils/helper') test('GrantManager with empty configuration', (t) => { - t.plan(1); + t.plan(1) t.throws(function () { - getManager(undefined); - }, Error); -}); + getManager(undefined) + }, Error) +}) test('GrantManager with rogue configuration', (t) => { - t.plan(4); - const rogueManager = getManager({}); - t.equal(rogueManager.access_token, undefined); - t.equal(rogueManager.client_id, undefined); - t.equal(rogueManager.publicKey, undefined); - t.equal(rogueManager.secret, undefined); -}); + t.plan(4) + const rogueManager = getManager({}) + t.equal(rogueManager.access_token, undefined) + t.equal(rogueManager.client_id, undefined) + t.equal(rogueManager.publicKey, undefined) + t.equal(rogueManager.secret, undefined) +}) test('GrantManager in public mode should be able to obtain a grant', (t) => { - t.plan(1); - const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json'); + t.plan(1) + const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json') manager.obtainDirectly('test-user', 'tiger') - .then((grant) => t.notEqual(grant.access_token, undefined)); -}); + .then((grant) => t.notEqual(grant.access_token, undefined)) +}) test('GrantManager in public mode should be able to obtain a raw grant', (t) => { - t.plan(1); - const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json'); + t.plan(1) + const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json') manager.obtainDirectly('test-user', 'tiger') - .then((grant) => t.notEqual(grant.toString(), undefined)); -}); + .then((grant) => t.notEqual(grant.toString(), undefined)) +}) test('GrantManager in public mode with public key configured should be able to obtain a grant', (t) => { - t.plan(1); - const manager = getManager('./test/fixtures/auth-utils/keycloak-with-public-key.json'); + t.plan(1) + const manager = getManager('./test/fixtures/auth-utils/keycloak-with-public-key.json') manager.obtainDirectly('test-user', 'tiger') - .then((grant) => t.notEqual(grant.access_token, undefined)); -}); + .then((grant) => t.notEqual(grant.access_token, undefined)) +}) test('GrantManager in public mode should be able to refresh a grant', (t) => { - t.plan(1); - const manager = getManager('./test/fixtures/auth-utils/keycloak-with-public-key.json'); + t.plan(1) + const manager = getManager('./test/fixtures/auth-utils/keycloak-with-public-key.json') manager.obtainDirectly('test-user', 'tiger') - .then((grant) => t.true(manager.isGrantRefreshable(grant))); -}); + .then((grant) => t.true(manager.isGrantRefreshable(grant))) +}) test('GrantManager should return empty with public key configured but invalid signature', (t) => { - t.plan(1); - const manager = getManager('./test/fixtures/auth-utils/keycloak-with-public-key.json'); + t.plan(1) + const manager = getManager('./test/fixtures/auth-utils/keycloak-with-public-key.json') manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.signature = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; - return manager.validateToken(grant.access_token, 'Bearer'); + grant.access_token.signature = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' + return manager.validateToken(grant.access_token, 'Bearer') }) .catch((e) => { - t.equal(e.message, 'invalid token (signature)'); - }); -}); + t.equal(e.message, 'invalid token (signature)') + }) +}) test('GrantManager in public mode should be able to get userinfo', (t) => { - t.plan(1); - const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json'); + t.plan(1) + const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json') manager.obtainDirectly('test-user', 'tiger') .then((grant) => manager.userInfo(grant.access_token)) - .then((user) => t.equal(user.preferred_username, 'test-user')); -}); + .then((user) => t.equal(user.preferred_username, 'test-user')) +}) test('GrantManager in public mode should fail if audience of ID token is not valid', (t) => { - t.plan(2); - const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json'); + t.plan(2) + const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json') manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.id_token.content.aud = []; - return manager.validateGrant(grant); + grant.id_token.content.aud = [] + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager in public mode should fail if audience of ID token is not valid with a dummy client in single array', (t) => { - t.plan(2); - const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json'); + t.plan(2) + const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json') manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.id_token.content.aud = ['public-client-dummy']; - return manager.validateGrant(grant); + grant.id_token.content.aud = ['public-client-dummy'] + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager in public mode should fail if audience of ID token is not valid with a dummy client in strings', (t) => { - t.plan(2); - const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json'); + t.plan(2) + const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json') manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.id_token.content.aud = 'public-client-dummy'; - return manager.validateGrant(grant); + grant.id_token.content.aud = 'public-client-dummy' + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager in public mode should fail if authorized party for ID token is not valid', (t) => { - t.plan(2); - const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json'); + t.plan(2) + const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json') manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.id_token.content.azp = []; - return manager.validateGrant(grant); + grant.id_token.content.azp = [] + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (authorized party should match client id)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (authorized party should match client id)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager in public mode should fail if audience of Access token is not valid', (t) => { - t.plan(2); - const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json'); + t.plan(2) + const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json') manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.content.aud = []; - return manager.validateGrant(grant); + grant.access_token.content.aud = [] + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) -const manager = getManager('./test/fixtures/auth-utils/keycloak-confidential.json'); +const manager = getManager('./test/fixtures/auth-utils/keycloak-confidential.json') test('GrantManager in confidential mode should be able to get userinfo', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => manager.userInfo(grant.access_token)) - .then((user) => t.equal(user.preferred_username, 'test-user')); -}); + .then((user) => t.equal(user.preferred_username, 'test-user')) +}) test('GrantManager in confidential mode should be able to obtain a grant', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') - .then((grant) => t.notEqual(grant.access_token, undefined)); -}); + .then((grant) => t.notEqual(grant.access_token, undefined)) +}) test('GrantManager in confidential mode should be able to refresh a grant', (t) => { - t.plan(4); - let originalAccessToken; + t.plan(4) + let originalAccessToken manager.obtainDirectly('test-user', 'tiger') .then(delay(3000)) .then((grant) => { - t.notEqual(grant.access_token, undefined); - t.true(manager.isGrantRefreshable(grant)); - originalAccessToken = grant.access_token; - return grant; + t.notEqual(grant.access_token, undefined) + t.true(manager.isGrantRefreshable(grant)) + originalAccessToken = grant.access_token + return grant }) .then((grant) => manager.ensureFreshness(grant)) .then((grant) => { - t.notEqual(grant.access_token, undefined); - t.notEqual(grant.access_token.token, originalAccessToken.token); - }); -}); + t.notEqual(grant.access_token, undefined) + t.notEqual(grant.access_token.token, originalAccessToken.token) + }) +}) test('GrantManager in confidential mode should be able to validate a valid token', (t) => { - t.plan(2); - let originalAccessToken; + t.plan(2) + let originalAccessToken manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - originalAccessToken = grant.access_token; - return manager.validateAccessToken(grant.access_token); + originalAccessToken = grant.access_token + return manager.validateAccessToken(grant.access_token) }) .then((token) => { - t.notEqual(token, undefined); - t.equal(token, originalAccessToken); - }); -}); + t.notEqual(token, undefined) + t.equal(token, originalAccessToken) + }) +}) test('GrantManager in confidential mode should be able to validate an invalid token', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then(delay(3000)) .then((grant) => manager.validateAccessToken(grant.access_token)) - .then((result) => t.equal(result, false)); -}); + .then((result) => t.equal(result, false)) +}) test('GrantManager in confidential mode should be able to validate a token has an invalid signature', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.token = grant.access_token.token.replace(/(.+?\..+?\.).*/, '$1.InvalidSignatureIsHereAgain'); - return manager.validateAccessToken(grant.access_token); + grant.access_token.token = grant.access_token.token.replace(/(.+?\..+?\.).*/, '$1.InvalidSignatureIsHereAgain') + return manager.validateAccessToken(grant.access_token) }) - .then((result) => t.equal(result, false)); -}); + .then((result) => t.equal(result, false)) +}) test('GrantManager in confidential mode should be able to validate a valid token string', (t) => { - t.plan(2); - let originalAccessToken; + t.plan(2) + let originalAccessToken manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - originalAccessToken = grant.access_token.token; - return manager.validateAccessToken(grant.access_token.token); + originalAccessToken = grant.access_token.token + return manager.validateAccessToken(grant.access_token.token) }) .then((token) => { - t.notEqual(token, undefined); - t.equal(token, originalAccessToken); - }); -}); + t.notEqual(token, undefined) + t.equal(token, originalAccessToken) + }) +}) test('GrantManager in confidential mode should be able to validate an invalid token string', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then(delay(3000)) .then((grant) => { - return manager.validateAccessToken(grant.access_token.token); + return manager.validateAccessToken(grant.access_token.token) }) - .then((result) => t.equal(result, false)); -}); + .then((result) => t.equal(result, false)) +}) test('GrantManager in confidential mode should be able to obtain a service account grant', (t) => { - t.plan(1); + t.plan(1) manager.obtainFromClientCredentials() .then(delay(3000)) .then((grant) => { - return manager.validateAccessToken(grant.access_token.token); + return manager.validateAccessToken(grant.access_token.token) }) - .then((result) => t.equal(result, false)); -}); + .then((result) => t.equal(result, false)) +}) test('GrantManager in confidential mode should fail if audience of ID token is not valid', (t) => { - t.plan(2); + t.plan(2) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.id_token.content.aud = []; - return manager.validateGrant(grant); + grant.id_token.content.aud = [] + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager in confidential mode should fail if audience of ID token is not valid with a dummy client in strings', (t) => { - t.plan(2); - const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json'); + t.plan(2) + const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json') manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.id_token.content.aud = 'confidential-client-dummy'; - return manager.validateGrant(grant); + grant.id_token.content.aud = 'confidential-client-dummy' + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager in confidential mode should fail if audience of ID token is not valid with a dummy client in single array', (t) => { - t.plan(2); - const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json'); + t.plan(2) + const manager = getManager('./test/fixtures/auth-utils/keycloak-public.json') manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.id_token.content.aud = ['confidential-client-dummy']; - return manager.validateGrant(grant); + grant.id_token.content.aud = ['confidential-client-dummy'] + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager in confidential mode should fail if authorized party for ID token is not valid', (t) => { - t.plan(2); + t.plan(2) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.id_token.content.azp = []; - return manager.validateGrant(grant); + grant.id_token.content.azp = [] + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (authorized party should match client id)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (authorized party should match client id)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager in confidential mode should fail if audience of Access token is not valid', (t) => { - t.plan(2); + t.plan(2) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.content.aud = []; - return manager.validateGrant(grant); + grant.access_token.content.aud = [] + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong audience)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager should be able to validate tokens in a grant', (t) => { - t.plan(4); - let originalAccessToken, originalRefreshToken, orginalIdToken; + t.plan(4) + let originalAccessToken, originalRefreshToken, orginalIdToken manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - originalAccessToken = grant.access_token; - originalRefreshToken = grant.refresh_token; - orginalIdToken = grant.id_token; - return manager.validateGrant(grant); + originalAccessToken = grant.access_token + originalRefreshToken = grant.refresh_token + orginalIdToken = grant.id_token + return manager.validateGrant(grant) }) .then((grant) => { - t.notEqual(grant.access_token, undefined); - t.equal(grant.access_token, originalAccessToken); - t.equal(grant.refresh_token, originalRefreshToken); - t.equal(grant.id_token, orginalIdToken); - }); -}); + t.notEqual(grant.access_token, undefined) + t.equal(grant.access_token, originalAccessToken) + t.equal(grant.refresh_token, originalRefreshToken) + t.equal(grant.id_token, orginalIdToken) + }) +}) test('GrantManager should be able to remove invalid tokens from a grant', (t) => { - t.plan(2); + t.plan(2) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.signature = Buffer.from('this signature is invalid'); - grant.refresh_token.signature = Buffer.from('this signature is also invalid'); - grant.id_token.signature = Buffer.from('this signature is still invalid'); - return manager.validateGrant(grant); + grant.access_token.signature = Buffer.from('this signature is invalid') + grant.refresh_token.signature = Buffer.from('this signature is also invalid') + grant.id_token.signature = Buffer.from('this signature is still invalid') + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (public key signature)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (public key signature)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager should reject with token missing error when bearer only', (t) => { - t.plan(2); - const originalBearerOnly = manager.bearerOnly; - manager.bearerOnly = true; + t.plan(2) + const originalBearerOnly = manager.bearerOnly + manager.bearerOnly = true manager.createGrant('{ }') .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (missing)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (missing)') }) .then((grant) => { - t.equal(grant, undefined); + t.equal(grant, undefined) }) .then((x) => { - manager.bearerOnly = originalBearerOnly; - }); -}); + manager.bearerOnly = originalBearerOnly + }) +}) test('GrantManager should not be able to refresh a grant when bearer only', (t) => { - t.plan(1); - const originalBearerOnly = manager.bearerOnly; - manager.bearerOnly = true; + t.plan(1) + const originalBearerOnly = manager.bearerOnly + manager.bearerOnly = true try { - t.false(manager.isGrantRefreshable({ refresh_token: 'a_refresh_token' })); + t.false(manager.isGrantRefreshable({ refresh_token: 'a_refresh_token' })) } finally { - manager.bearerOnly = originalBearerOnly; + manager.bearerOnly = originalBearerOnly } -}); +}) test('GrantManager should reject with refresh token missing error', (t) => { - t.plan(2); + t.plan(2) manager.ensureFreshness({ isExpired: () => true }) .catch((e) => { - t.equal(e.message, 'Unable to refresh without a refresh token'); + t.equal(e.message, 'Unable to refresh without a refresh token') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager validate empty access token', (t) => { - t.plan(1); + t.plan(1) manager.validateAccessToken('') .then((result) => { - t.equal(result, false); - }); -}); + t.equal(result, false) + }) +}) test('GrantManager return user realm role', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - t.true(grant.access_token.hasRealmRole('user')); - }); -}); + t.true(grant.access_token.hasRealmRole('user')) + }) +}) test('GrantManager validate non existent role', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - t.false(grant.access_token.hasRealmRole('')); - }); -}); + t.false(grant.access_token.hasRealmRole('')) + }) +}) test('GrantManager should be false for user with no realm level roles', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.content.realm_access = {}; - t.false(grant.access_token.hasRealmRole('test')); - }); -}); + grant.access_token.content.realm_access = {} + t.false(grant.access_token.hasRealmRole('test')) + }) +}) test('GrantManager validate non existent role app', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - t.false(grant.access_token.hasRole('')); - }); -}); + t.false(grant.access_token.hasRole('')) + }) +}) test('GrantManager validate existent role app', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - t.true(grant.access_token.hasRole('test')); - }); -}); + t.true(grant.access_token.hasRole('test')) + }) +}) test('GrantManager validate role app with empty clientId', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.clientId = ''; - t.false(grant.access_token.hasRole('test')); - }); -}); + grant.access_token.clientId = '' + t.false(grant.access_token.hasRole('test')) + }) +}) test('GrantManager validate empty role app', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - t.false(grant.access_token.hasApplicationRole('', '')); - }); -}); + t.false(grant.access_token.hasApplicationRole('', '')) + }) +}) test('GrantManager return user realm role based on realm name', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - t.true(grant.access_token.hasRole('realm:user')); - }); -}); + t.true(grant.access_token.hasRole('realm:user')) + }) +}) test('GrantManager in confidential mode should use callback if provided and validate access token', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { manager.validateAccessToken(grant.access_token, function (err, result) { if (err) { - t.end(err); + t.end(err) } - t.equal(result, grant.access_token); - }); - }); -}); + t.equal(result, grant.access_token) + }) + }) +}) test('GrantManager should be able to remove expired access_token token and keep others', (t) => { - t.plan(2); + t.plan(2) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.content.exp = 0; - return manager.validateGrant(grant); + grant.access_token.content.exp = 0 + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (expired)'); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (expired)') }) .then((grant) => { - t.equal(grant, undefined); - }); -}); + t.equal(grant, undefined) + }) +}) test('GrantManager should return empty when trying to obtain from code with empty params', (t) => { - t.plan(1); + t.plan(1) manager.obtainFromCode('', '', '', '', function () {}) .then((result) => { - t.equal(result, undefined); - }); -}); + t.equal(result, undefined) + }) +}) test('GrantManager should raise an error when trying to obtain from code with rogue params', (t) => { - t.plan(1); + t.plan(1) manager.obtainFromCode('', '', '', '', {}) .catch((e) => { - t.equal(e.message, '400:Bad Request'); - }); -}); + t.equal(e.message, '400:Bad Request') + }) +}) test('GrantManager should be able to validate invalid ISS', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.content.iss = 'http://wrongiss.com'; - return manager.validateGrant(grant); + grant.access_token.content.iss = 'http://wrongiss.com' + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong ISS)'); - }); -}); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (wrong ISS)') + }) +}) test('GrantManager should be able to validate invalid iat', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.content.iat = -5; - return manager.validateGrant(grant); + grant.access_token.content.iat = -5 + return manager.validateGrant(grant) }) .catch((e) => { - t.equal(e.message, 'Grant validation failed. Reason: invalid token (stale token)'); - }); -}); + t.equal(e.message, 'Grant validation failed. Reason: invalid token (stale token)') + }) +}) test('GrantManager should be ensure that a grant is fresh', (t) => { - t.plan(1); - let originalGrant; + t.plan(1) + let originalGrant manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - originalGrant = Object.assign({}, grant); - return manager.ensureFreshness(grant); + originalGrant = Object.assign({}, grant) + return manager.ensureFreshness(grant) }) .then((result) => { - t.notEqual(result, originalGrant); - }); -}); + t.notEqual(result, originalGrant) + }) +}) test('GrantManager should raise an error when access token and refresh token do not exist', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token = undefined; - grant.refresh_token = undefined; - return manager.ensureFreshness(grant); + grant.access_token = undefined + grant.refresh_token = undefined + return manager.ensureFreshness(grant) }) .catch(e => { - t.equal(e.message, 'Unable to refresh without a refresh token'); - }); -}); + t.equal(e.message, 'Unable to refresh without a refresh token') + }) +}) test('GrantManager should validate unsigned token', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.signed = false; - return manager.validateToken(grant.access_token, 'Bearer'); + grant.access_token.signed = false + return manager.validateToken(grant.access_token, 'Bearer') }) .catch(e => { - t.equal(e.message, 'invalid token (not signed)'); - }); -}); + t.equal(e.message, 'invalid token (not signed)') + }) +}) test('GrantManager should not validate token with wrong type', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - return manager.validateToken(grant.access_token, 'Refresh'); + return manager.validateToken(grant.access_token, 'Refresh') }) .catch(e => { - t.equal(e.message, 'invalid token (wrong type)'); - }); -}); + t.equal(e.message, 'invalid token (wrong type)') + }) +}) test('GrantManager should fail to load public key when kid is empty', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.header.kid = {}; - return manager.validateToken(grant.access_token, 'Bearer'); + grant.access_token.header.kid = {} + return manager.validateToken(grant.access_token, 'Bearer') }) .catch(e => { - t.equal(e.message, 'failed to load public key to verify token. Reason: Expected "jwk" to be an Object'); - }); -}); + t.equal(e.message, 'failed to load public key to verify token. Reason: Expected "jwk" to be an Object') + }) +}) test('GrantManager should fail with invalid signature', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.signature = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; - return manager.validateToken(grant.access_token, 'Bearer'); + grant.access_token.signature = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' + return manager.validateToken(grant.access_token, 'Bearer') }) .catch(e => { - t.equal(e.message, 'invalid token (public key signature)'); - }); -}); + t.equal(e.message, 'invalid token (public key signature)') + }) +}) test('GrantManager should return false when resource_access is undefined', (t) => { - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') .then((grant) => { - grant.access_token.content = {}; - t.false(grant.access_token.hasApplicationRole('test')); - }); -}); + grant.access_token.content = {} + t.false(grant.access_token.hasApplicationRole('test')) + }) +}) test('GrantManager#validateToken returns undefined for an invalid token', (t) => { - t.plan(4); + t.plan(4) const expiredToken = { isExpired: () => true - }; + } const unsignedToken = { isExpired: () => false, signed: undefined - }; + } const notBeforeToken = { isExpired: () => false, signed: true, content: { iat: -1 } - }; - const manager = getManager('./test/fixtures/auth-utils/keycloak-https.json'); + } + const manager = getManager('./test/fixtures/auth-utils/keycloak-https.json') const tokens = [ undefined, expiredToken, unsignedToken, notBeforeToken - ]; + ] /* jshint loopfunc:true */ for (const token of tokens) { manager.validateToken(token, 'Bearer') .catch((err) => { - t.true(err instanceof Error, err.message); - }); + t.true(err instanceof Error, err.message) + }) } -}); +}) test('GrantManager#obtainDirectly should work with https', (t) => { nock('https://localhost:8080') @@ -667,19 +667,19 @@ test('GrantManager#obtainDirectly should work with https', (t) => { grant_type: 'password', scope: 'openid' }) - .reply(204, helper.dummyReply); - const manager = getManager('./test/fixtures/auth-utils/keycloak-https.json'); - manager.validateToken = (t) => { return Promise.resolve(t); }; - manager.ensureFreshness = (t) => { return Promise.resolve(t); }; + .reply(204, helper.dummyReply) + const manager = getManager('./test/fixtures/auth-utils/keycloak-https.json') + manager.validateToken = (t) => { return Promise.resolve(t) } + manager.ensureFreshness = (t) => { return Promise.resolve(t) } - t.plan(1); + t.plan(1) manager.obtainDirectly('test-user', 'tiger') - .then((grant) => t.equal(grant.access_token.token, 'Dummy access token')); -}); + .then((grant) => t.equal(grant.access_token.token, 'Dummy access token')) +}) test('GrantManager#ensureFreshness should fetch new access token with client id ', (t) => { - t.plan(1); + t.plan(1) const refreshedToken = { access_token: 'some.access.token', @@ -690,30 +690,30 @@ test('GrantManager#ensureFreshness should fetch new access token with client id id_token: 'some-id-token', 'not-before-policy': 1462208947, session_state: 'ess-sion-tat-se' - }; + } nock('http://localhost:8180') .post('/realms/nodejs-test-mock/protocol/openid-connect/token', { grant_type: 'refresh_token', client_id: 'public-client', refresh_token: 'i-Am-The-Refresh-Token' }) - .reply(204, refreshedToken); + .reply(204, refreshedToken) const grant = { isExpired: function () { - return true; + return true }, refresh_token: { token: 'i-Am-The-Refresh-Token', isExpired: () => false } - }; + } - const manager = getManager('./test/fixtures/auth-utils/keycloak-public-mock.json'); - manager.createGrant = (t) => { return Promise.resolve(t); }; + const manager = getManager('./test/fixtures/auth-utils/keycloak-public-mock.json') + manager.createGrant = (t) => { return Promise.resolve(t) } manager.ensureFreshness(grant) .then((grant) => { - t.true(grant === JSON.stringify(refreshedToken)); - }); -}); + t.true(grant === JSON.stringify(refreshedToken)) + }) +}) diff --git a/test/grant-manager-token-timeout-spec.js b/test/grant-manager-token-timeout-spec.js index 724f203e..29904ac6 100644 --- a/test/grant-manager-token-timeout-spec.js +++ b/test/grant-manager-token-timeout-spec.js @@ -1,55 +1,55 @@ -'use strict'; +'use strict' -const GrantManager = require('../middleware/auth-utils/grant-manager'); -const Config = require('../middleware/auth-utils/config'); -const test = require('tape'); -const delay = (ms) => (value) => new Promise((resolve) => setTimeout(() => resolve(value), ms)); -const getManager = (fixture) => new GrantManager(new Config(fixture)); +const GrantManager = require('../middleware/auth-utils/grant-manager') +const Config = require('../middleware/auth-utils/config') +const test = require('tape') +const delay = (ms) => (value) => new Promise((resolve) => setTimeout(() => resolve(value), ms)) +const getManager = (fixture) => new GrantManager(new Config(fixture)) test('GrantManager should be able to refresh token after accessTokenLifespan', (t) => { - const manager = getManager('./test/fixtures/auth-utils/keycloak-token-test.json'); + const manager = getManager('./test/fixtures/auth-utils/keycloak-token-test.json') manager.obtainDirectly('bburke@redhat.com', 'password') .then((grant) => { return manager.validateAccessToken(grant.access_token).then(firstToken => { - t.notEqual(firstToken, false); + t.notEqual(firstToken, false) return manager.ensureFreshness(grant) .then(grant => { - t.equal(grant.access_token, firstToken); - return grant; + t.equal(grant.access_token, firstToken) + return grant }) .then(delay(10000)) .then(grant => { - t.true(grant.access_token.isExpired()); - return grant; + t.true(grant.access_token.isExpired()) + return grant }) .then(grant => { return manager.ensureFreshness(grant).then(grant => { return manager.validateAccessToken(grant.access_token) .then(refreshedToken => { - t.notEqual(refreshedToken, false); - t.notEqual(refreshedToken, firstToken); - }); - }); - }); - }); + t.notEqual(refreshedToken, false) + t.notEqual(refreshedToken, firstToken) + }) + }) + }) + }) }) - .then(t.end); -}); + .then(t.end) +}) test('GrantManager should not be able to refresh token after ssoSessionIdleTimeout', (t) => { - const manager = getManager('./test/fixtures/auth-utils/keycloak-token-test.json'); + const manager = getManager('./test/fixtures/auth-utils/keycloak-token-test.json') manager.obtainDirectly('bburke@redhat.com', 'password') .then((grant) => { return manager.validateAccessToken(grant.access_token).then(firstToken => { - t.notEqual(firstToken, false); - return grant; + t.notEqual(firstToken, false) + return grant }) .then(delay(15000 + 120000)) // 15 second ssoSessionIdleTimeout + 120s IDLE_TIMEOUT_WINDOW_SECONDS from https://github.com/keycloak/keycloak/blob/master/server-spi-private/src/main/java/org/keycloak/models/utils/SessionTimeoutHelper.java .then(grant => manager.ensureFreshness(grant)) .catch((e) => { - t.equal(e.message, 'Unable to refresh with expired refresh token'); - }); + t.equal(e.message, 'Unable to refresh with expired refresh token') + }) }) - .then(t.end); -}); + .then(t.end) +}) diff --git a/test/keycloak-connect-rest-enforcer-spec.js b/test/keycloak-connect-rest-enforcer-spec.js index fe179a54..e52bfc31 100644 --- a/test/keycloak-connect-rest-enforcer-spec.js +++ b/test/keycloak-connect-rest-enforcer-spec.js @@ -13,177 +13,177 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const admin = require('./utils/realm'); -const NodeApp = require('./fixtures/node-console/index').NodeApp; +const admin = require('./utils/realm') +const NodeApp = require('./fixtures/node-console/index').NodeApp -const test = require('blue-tape'); -const axios = require('axios'); -const getToken = require('./utils/token'); +const test = require('blue-tape') +const axios = require('axios') +const getToken = require('./utils/token') -const realmName = 'policy-enforcer-realm'; -const realmManager = admin.createRealm(realmName); -const app = new NodeApp(); +const realmName = 'policy-enforcer-realm' +const realmManager = admin.createRealm(realmName) +const app = new NodeApp() test('setup', t => { return realmManager.then(() => { return admin.createClient(app.enforcerResourceServer(), realmName) .then((installation) => { - return app.build(installation); - }); - }); -}); + return app.build(installation) + }) + }) +}) test('Should test access to protected resource and scope view.', t => { - t.plan(4); + t.plan(4) return getToken({ realmName }).then((token) => { const opt = { method: 'get', url: `${app.address}/protected/enforcer/resource`, headers: { Authorization: `Bearer ${token}` } - }; + } return axios(opt) .then(response => { - t.equal(response.data.message, 'resource:view'); - t.equal(response.data.permissions.length, 1); - t.equal(response.data.permissions[0].rsname, 'resource'); - t.equal(response.data.permissions[0].scopes[0], 'view'); + t.equal(response.data.message, 'resource:view') + t.equal(response.data.permissions.length, 1) + t.equal(response.data.permissions[0].rsname, 'resource') + t.equal(response.data.permissions[0].scopes[0], 'view') }) .catch(error => { - t.fail(error.response.data); - }); - }); -}); + t.fail(error.response.data) + }) + }) +}) test('Should test access to protected resource and scope view without authorization header.', t => { - t.plan(1); + t.plan(1) return getToken({ realmName }).then((token) => { const opt = { method: 'get', url: `${app.address}/protected/enforcer/resource` - }; + } return axios(opt) .then(_ => {}) .catch(error => { - t.equal(error.response.data, 'Access denied'); - }); - }); -}); + t.equal(error.response.data, 'Access denied') + }) + }) +}) test('Should test access to protected resource and scope update - and returned permissions.', t => { - t.plan(4); + t.plan(4) return getToken({ realmName }).then((token) => { const opt = { method: 'post', url: `${app.address}/protected/enforcer/resource`, headers: { Authorization: `Bearer ${token}` } - }; + } return axios(opt) .then(response => { - t.equal(response.data.message, 'resource:update'); - t.equal(response.data.permissions.length, 1); - t.equal(response.data.permissions[0].rsname, 'resource'); - t.equal(response.data.permissions[0].scopes[0], 'update'); - }); - }); -}); + t.equal(response.data.message, 'resource:update') + t.equal(response.data.permissions.length, 1) + t.equal(response.data.permissions[0].rsname, 'resource') + t.equal(response.data.permissions[0].scopes[0], 'update') + }) + }) +}) test('Should test no access to protected resource and scope delete.', t => { - t.plan(2); + t.plan(2) return getToken({ realmName }).then((token) => { const opt = { method: 'delete', url: `${app.address}/protected/enforcer/resource`, headers: { Authorization: `Bearer ${token}` } - }; + } return axios(opt) .then(_ => {}) .catch(error => { - t.equal(error.response.data.permissions, undefined); - t.equal(error.response.data, 'Access denied'); - }); - }); -}); + t.equal(error.response.data.permissions, undefined) + t.equal(error.response.data, 'Access denied') + }) + }) +}) test('Should test no access to protected resource and scope view and delete.', t => { - t.plan(2); + t.plan(2) return getToken({ realmName }).then((token) => { const opt = { method: 'get', url: `${app.address}/protected/enforcer/resource-view-delete`, headers: { Authorization: `Bearer ${token}` } - }; + } return axios(opt) .then(_ => {}) .catch(error => { - t.equal(error.response.data.permissions, undefined); - t.equal(error.response.data, 'Access denied'); - }); - }); -}); + t.equal(error.response.data.permissions, undefined) + t.equal(error.response.data, 'Access denied') + }) + }) +}) test('Should test access to protected resource pushing claims.', t => { - t.plan(4); + t.plan(4) return getToken({ realmName }).then((token) => { const opt = { method: 'get', url: `${app.address}/protected/enforcer/resource-claims?user_agent=mozilla`, headers: { Authorization: `Bearer ${token}` } - }; + } return axios(opt) .then(response => { - t.equal(response.data.message, 'mozilla'); - t.equal(response.data.permissions[0].rsname, 'photo'); - t.equal(response.data.permissions[0].claims.user_agent.length, 1); - t.equal(response.data.permissions[0].claims.user_agent[0], 'mozilla'); + t.equal(response.data.message, 'mozilla') + t.equal(response.data.permissions[0].rsname, 'photo') + t.equal(response.data.permissions[0].claims.user_agent.length, 1) + t.equal(response.data.permissions[0].claims.user_agent[0], 'mozilla') }) .catch(error => { - t.fail(error.response.data); - }); - }); -}); + t.fail(error.response.data) + }) + }) +}) test('Should test no access to protected resource wrong claims.', t => { - t.plan(2); + t.plan(2) return getToken({ realmName }).then((token) => { const opt = { method: 'get', url: `${app.address}/protected/enforcer/resource-claims?user_agent=ie`, headers: { Authorization: `Bearer ${token}` } - }; + } return axios(opt) .then(_ => {}) .catch(error => { - t.equal(error.response.data.permissions, undefined); - t.equal(error.response.data, 'Access denied'); - }); - }); -}); + t.equal(error.response.data.permissions, undefined) + t.equal(error.response.data, 'Access denied') + }) + }) +}) test('Should test access to resources without any permission defined.', t => { - t.plan(2); + t.plan(2) return getToken({ realmName }).then((token) => { const opt = { method: 'get', url: `${app.address}/protected/enforcer/no-permission-defined`, headers: { Authorization: `Bearer ${token}` } - }; + } return axios(opt) .then(response => { - t.equal(response.data.message, 'always grant'); - t.equal(response.data.permissions, undefined); + t.equal(response.data.message, 'always grant') + t.equal(response.data.permissions, undefined) }) .catch(error => { - t.fail(error.response.data); - }); - }); -}); + t.fail(error.response.data) + }) + }) +}) test('teardown', t => { return realmManager.then((realm) => { - app.destroy(); - admin.destroy(realmName); - }); -}); + app.destroy() + admin.destroy(realmName) + }) +}) diff --git a/test/keycloak-connect-rest-mixed-client-spec.js b/test/keycloak-connect-rest-mixed-client-spec.js index 85559f8e..bf6b6e3f 100644 --- a/test/keycloak-connect-rest-mixed-client-spec.js +++ b/test/keycloak-connect-rest-mixed-client-spec.js @@ -13,119 +13,119 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const admin = require('./utils/realm'); -const NodeApp = require('./fixtures/node-console/index').NodeApp; -const TestVector = require('./utils/helper').TestVector; +const admin = require('./utils/realm') +const NodeApp = require('./fixtures/node-console/index').NodeApp +const TestVector = require('./utils/helper').TestVector -const test = require('blue-tape'); -const axios = require('axios'); -const getToken = require('./utils/token'); +const test = require('blue-tape') +const axios = require('axios') +const getToken = require('./utils/token') -const realmName = 'mixed-mode-realm'; -const realmManager = admin.createRealm(realmName); -const app = new NodeApp(); +const realmName = 'mixed-mode-realm' +const realmManager = admin.createRealm(realmName) +const app = new NodeApp() const auth = { username: 'test-admin', password: 'password' -}; +} -const getSessionCookie = response => response.headers['set-cookie'][0].split(';')[0]; +const getSessionCookie = response => response.headers['set-cookie'][0].split(';')[0] const assertAlternativeMessages = (assert, message, ...messages) => { - assert.true(messages.includes(message), `Message "${message}" is not included in provided messages`); -}; + assert.true(messages.includes(message), `Message "${message}" is not included in provided messages`) +} test('setup', t => { return realmManager.then(() => { return admin.createClient(app.confidential(), realmName) .then((installation) => { - return app.build(installation); - }); - }); -}); + return app.build(installation) + }) + }) +}) test('Should test protected route.', t => { - t.plan(1); + t.plan(1) const opt = { url: `${app.address}/service/admin` - }; - return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for no credentials'); -}); + } + return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for no credentials') +}) test('Should test protected route with admin credentials.', t => { - t.plan(1); + t.plan(1) return getToken({ realmName }).then((token) => { const opt = { url: `${app.address}/service/admin`, headers: { Authorization: `Bearer ${token}` } - }; + } return axios(opt) .then(response => { - t.equal(response.data.message, 'admin'); + t.equal(response.data.message, 'admin') }) .catch(error => { - t.fail(error.response.data); - }); - }); -}); + t.fail(error.response.data) + }) + }) +}) test('Should test protected route with invalid access token.', t => { - t.plan(1); + t.plan(1) return getToken({ realmName }).then((token) => { const opt = { url: `${app.address}/service/admin`, headers: { Authorization: 'Bearer ' + token.replace(/(.+?\..+?\.).*/, '$1.Invalid') } - }; - return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for invalid access token'); - }); -}); + } + return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for invalid access token') + }) +}) test('Should handle direct access grants.', t => { - t.plan(3); + t.plan(3) return axios.post(`${app.address}/service/grant`, auth) .then(response => { - t.ok(response.data.id_token, 'Response should contain an id_token'); - t.ok(response.data.access_token, 'Response should contain an access_token'); - t.ok(response.data.refresh_token, 'Response should contain an refresh_token'); + t.ok(response.data.id_token, 'Response should contain an id_token') + t.ok(response.data.access_token, 'Response should contain an access_token') + t.ok(response.data.refresh_token, 'Response should contain an refresh_token') }) .catch(error => { - t.fail(error.response.data); - }); -}); + t.fail(error.response.data) + }) +}) test('Should store the grant.', t => { - t.plan(3); - const endpoint = `${app.address}/service/grant`; + t.plan(3) + const endpoint = `${app.address}/service/grant` return axios.post(endpoint, auth) .then(response => getSessionCookie(response)) .then(cookie => { return axios.get(endpoint, { headers: { cookie } }) .then(response => { - t.ok(response.data.id_token, 'Response should contain an id_token'); - t.ok(response.data.access_token, 'Response should contain an access_token'); - t.ok(response.data.refresh_token, 'Response should contain an refresh_token'); - }); - }); -}); + t.ok(response.data.id_token, 'Response should contain an id_token') + t.ok(response.data.access_token, 'Response should contain an access_token') + t.ok(response.data.refresh_token, 'Response should contain an refresh_token') + }) + }) +}) test('Should not store grant on bearer request', t => { - t.plan(4); - const endpoint = `${app.address}/service/grant`; - let sessionCookie; + t.plan(4) + const endpoint = `${app.address}/service/grant` + let sessionCookie return axios.post(endpoint, auth) .then(response => { const data = { cookie: getSessionCookie(response), grant: response.data - }; - sessionCookie = data.cookie; - return data; + } + sessionCookie = data.cookie + return data }) .then(data => { const opt = { @@ -134,221 +134,221 @@ test('Should not store grant on bearer request', t => { Authorization: 'Bearer ' + data.grant.access_token.token, Cookie: data.cookie } - }; + } return axios(opt) .then(response => { - t.equal(response.data.message, 'secured'); + t.equal(response.data.message, 'secured') const opt = { url: endpoint, headers: { Cookie: sessionCookie } - }; + } return axios(opt) .then(response => { - t.ok(response.data.id_token, 'Response should contain an id_token'); - t.ok(response.data.access_token, 'Response should contain an access_token'); - t.ok(response.data.refresh_token, 'Response should contain an refresh_token'); - }); - }); - }); -}); + t.ok(response.data.id_token, 'Response should contain an id_token') + t.ok(response.data.access_token, 'Response should contain an access_token') + t.ok(response.data.refresh_token, 'Response should contain an refresh_token') + }) + }) + }) +}) test('Should test admin logout endpoint with incomplete payload', t => { - t.plan(2); + t.plan(2) - const app = new NodeApp(); - const client = admin.createClient(app.confidential('adminapp'), realmName); + const app = new NodeApp() + const client = admin.createClient(app.confidential('adminapp'), realmName) return client.then((installation) => { - app.build(installation); + app.build(installation) const opt = { method: 'post', url: `${app.address}/k_logout`, data: TestVector.logoutIncompletePayload - }; + } return axios(opt).catch(err => { - t.equal(err.response.status, 401); + t.equal(err.response.status, 401) assertAlternativeMessages(t, err.response.data, "Cannot read property 'kid' of undefined", "Cannot read properties of undefined (reading 'kid')" - ); + ) - app.destroy(); - }); + app.destroy() + }) }).then(() => { - app.destroy(); - }); -}); + app.destroy() + }) +}) test('Should test admin logout endpoint with payload signed by a different key pair', t => { - t.plan(2); + t.plan(2) - const app = new NodeApp(); - const client = admin.createClient(app.confidential('adminapp2'), realmName); + const app = new NodeApp() + const client = admin.createClient(app.confidential('adminapp2'), realmName) return client.then((installation) => { - app.build(installation); + app.build(installation) const opt = { method: 'post', url: `${app.address}/k_logout`, data: TestVector.logoutWrongKeyPairPayload - }; + } return axios(opt).catch(err => { - t.equal(err.response.status, 401); - t.equal(err.response.data, 'admin request failed: invalid token (signature)'); - app.destroy(); - }); + t.equal(err.response.status, 401) + t.equal(err.response.data, 'admin request failed: invalid token (signature)') + app.destroy() + }) }).then(() => { - app.destroy(); - }); -}); + app.destroy() + }) +}) test('Should test admin logout endpoint with valid payload', t => { - t.plan(1); + t.plan(1) - const app = new NodeApp(); - const client = admin.createClient(app.confidential('adminapp3'), realmName); + const app = new NodeApp() + const client = admin.createClient(app.confidential('adminapp3'), realmName) return client.then((installation) => { - app.build(installation); + app.build(installation) const opt = { method: 'post', url: `${app.address}/k_logout`, data: TestVector.logoutValidPayload - }; + } return axios(opt).then(response => { - t.equal(response.status, 200); + t.equal(response.status, 200) }).catch(err => { - t.fail(err.response.data); - }); + t.fail(err.response.data) + }) }).then(() => { - app.destroy(); - }); -}); + app.destroy() + }) +}) test('Should test admin push_not_before endpoint with incomplete payload', t => { - t.plan(2); + t.plan(2) - const app = new NodeApp(); - const client = admin.createClient(app.confidential('adminapp5'), realmName); + const app = new NodeApp() + const client = admin.createClient(app.confidential('adminapp5'), realmName) return client.then((installation) => { - app.build(installation); + app.build(installation) const opt = { method: 'post', url: `${app.address}/k_push_not_before`, data: TestVector.notBeforeIncompletePayload - }; + } return axios(opt).catch(err => { - t.equal(err.response.status, 401); + t.equal(err.response.status, 401) assertAlternativeMessages(t, err.response.data, "Cannot read property 'kid' of undefined", "Cannot read properties of undefined (reading 'kid')" - ); + ) - app.destroy(); - }); + app.destroy() + }) }).then(() => { - app.destroy(); - }); -}); + app.destroy() + }) +}) test('Should test admin push_not_before endpoint with payload signed by a different key pair', t => { - t.plan(2); + t.plan(2) - const app = new NodeApp(); - const client = admin.createClient(app.confidential('adminapp6'), realmName); + const app = new NodeApp() + const client = admin.createClient(app.confidential('adminapp6'), realmName) return client.then((installation) => { - app.build(installation); + app.build(installation) const opt = { method: 'post', url: `${app.address}/k_push_not_before`, data: TestVector.notBeforeWrongKeyPairPayload - }; + } return axios(opt).catch(err => { - t.equal(err.response.status, 401); - t.equal(err.response.data, 'admin request failed: invalid token (signature)'); - app.destroy(); - }); + t.equal(err.response.status, 401) + t.equal(err.response.data, 'admin request failed: invalid token (signature)') + app.destroy() + }) }).then(() => { - app.destroy(); - }); -}); + app.destroy() + }) +}) test('Should verify during authentication if the token contains the client name as audience.', t => { - t.plan(3); - const someapp = new NodeApp(); - const client = admin.createClient(someapp.confidential('audience-app'), realmName); + t.plan(3) + const someapp = new NodeApp() + const client = admin.createClient(someapp.confidential('audience-app'), realmName) return client.then((installation) => { - installation.verifyTokenAudience = true; - someapp.build(installation); + installation.verifyTokenAudience = true + someapp.build(installation) return axios.post(`${someapp.address}/service/grant`, auth) .then(response => { - t.ok(response.data.id_token, 'Response should contain an id_token'); - t.ok(response.data.access_token, 'Response should contain an access_token'); - t.ok(response.data.refresh_token, 'Response should contain an refresh_token'); + t.ok(response.data.id_token, 'Response should contain an id_token') + t.ok(response.data.access_token, 'Response should contain an access_token') + t.ok(response.data.refresh_token, 'Response should contain an refresh_token') }) .catch(error => { - t.fail(error.response.data); - }); + t.fail(error.response.data) + }) }).then(() => { - someapp.destroy(); - }); -}); + someapp.destroy() + }) +}) test('Should test admin push_not_before endpoint with valid payload', t => { - t.plan(1); + t.plan(1) - const app = new NodeApp(); - const client = admin.createClient(app.confidential('adminapp7'), realmName); + const app = new NodeApp() + const client = admin.createClient(app.confidential('adminapp7'), realmName) return client.then((installation) => { - app.build(installation); + app.build(installation) const opt = { method: 'post', url: `${app.address}/k_push_not_before`, data: TestVector.notBeforeValidPayload - }; + } return axios(opt).then(response => { - t.equal(response.status, 200); + t.equal(response.status, 200) }).catch(err => { - t.fail(err.response.data); - }); + t.fail(err.response.data) + }) }).then(() => { - app.destroy(); - }); -}); + app.destroy() + }) +}) test('Should logout with redirect url', t => { - t.plan(1); - const serviceEndpoint = `${app.address}/service/grant`; - const logoutEndpoint = `${app.address}/logout?redirect_url=http%3A%2F%2Flocalhost%3A${app.port}%2Fbye`; + t.plan(1) + const serviceEndpoint = `${app.address}/service/grant` + const logoutEndpoint = `${app.address}/logout?redirect_url=http%3A%2F%2Flocalhost%3A${app.port}%2Fbye` return axios.post(serviceEndpoint, auth) .then(response => getSessionCookie(response)) .then(cookie => { return axios.get(logoutEndpoint, { headers: { cookie } }) .then(response => { - t.assert(response.request.path, '/bye', 'Expected redirect after logout'); - }); - }); -}); + t.assert(response.request.path, '/bye', 'Expected redirect after logout') + }) + }) +}) test('teardown', t => { return realmManager.then((realm) => { - app.destroy(); - admin.destroy(realmName); - }); -}); + app.destroy() + admin.destroy(realmName) + }) +}) diff --git a/test/keycloak-connect-rest-spec.js b/test/keycloak-connect-rest-spec.js index c9c4b084..822d119b 100644 --- a/test/keycloak-connect-rest-spec.js +++ b/test/keycloak-connect-rest-spec.js @@ -13,82 +13,82 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const admin = require('./utils/realm'); -const TestVector = require('./utils/helper').TestVector; -const NodeApp = require('./fixtures/node-console/index').NodeApp; +const admin = require('./utils/realm') +const TestVector = require('./utils/helper').TestVector +const NodeApp = require('./fixtures/node-console/index').NodeApp -const test = require('blue-tape'); -const axios = require('axios'); -const getToken = require('./utils/token'); +const test = require('blue-tape') +const axios = require('axios') +const getToken = require('./utils/token') -const realmName = 'service-node-realm'; -const realmManager = admin.createRealm(realmName); -const app = new NodeApp(); +const realmName = 'service-node-realm' +const realmManager = admin.createRealm(realmName) +const app = new NodeApp() test('setup', t => { return realmManager.then(() => { return admin.createClient(app.bearerOnly(), realmName) .then((installation) => { - return app.build(installation); - }); - }); -}); + return app.build(installation) + }) + }) +}) test('Should test unprotected route.', t => { - t.plan(1); + t.plan(1) const opt = { method: 'get', url: `${app.address}/service/public` - }; + } return axios(opt) .then(response => { - t.equal(response.data.message, 'public'); + t.equal(response.data.message, 'public') }) .catch(error => { - t.fail(error.response.data); - }); -}); + t.fail(error.response.data) + }) +}) test('Should test protected route.', t => { - t.plan(1); + t.plan(1) const opt = { method: 'get', url: `${app.address}/service/admin` - }; - return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for no credentials'); -}); + } + return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for no credentials') +}) test('Should test for bad request on k_logout without any parameters.', t => { - t.plan(1); + t.plan(1) const opt = { method: 'get', url: `${app.address}/k_logout` - }; - return t.shouldFail(axios(opt), 'Response should be bad request'); -}); + } + return t.shouldFail(axios(opt), 'Response should be bad request') +}) test('Should test protected route with admin credentials.', t => { - t.plan(1); + t.plan(1) return getToken().then((token) => { const opt = { method: 'get', url: `${app.address}/service/admin`, headers: { Authorization: `Bearer ${token}` } - }; + } return axios(opt) .then(response => { - t.equal(response.data.message, 'admin'); + t.equal(response.data.message, 'admin') }) .catch(error => { - t.fail(error.response.data); - }); - }); -}); + t.fail(error.response.data) + }) + }) +}) test('Should test protected route with invalid access token.', t => { - t.plan(1); + t.plan(1) return getToken().then((token) => { const opt = { method: 'get', @@ -96,20 +96,20 @@ test('Should test protected route with invalid access token.', t => { headers: { Authorization: 'Bearer ' + token.replace(/(.+?\..+?\.).*/, '$1.Invalid') } - }; - return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for invalid access token'); - }); -}); + } + return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for invalid access token') + }) +}) test('Access should be denied for bearer client with invalid public key.', t => { - t.plan(1); + t.plan(1) - const someApp = new NodeApp(); - const client = admin.createClient(app.bearerOnly('wrongkey-app'), realmName); + const someApp = new NodeApp() + const client = admin.createClient(app.bearerOnly('wrongkey-app'), realmName) return client.then((installation) => { - installation['realm-public-key'] = TestVector.wrongRealmPublicKey; - someApp.build(installation); + installation['realm-public-key'] = TestVector.wrongRealmPublicKey + someApp.build(installation) return getToken().then((token) => { const opt = { @@ -118,27 +118,27 @@ test('Access should be denied for bearer client with invalid public key.', t => headers: { Authorization: 'Bearer ' + token } - }; + } - return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for invalid public key'); - }); + return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for invalid public key') + }) }).then(() => { - someApp.destroy(); + someApp.destroy() }) .catch(err => { - someApp.destroy(); - throw err; - }); -}); + someApp.destroy() + throw err + }) +}) test('Should test protected route after push revocation.', t => { - t.plan(2); + t.plan(2) - const app = new NodeApp(); - const client = admin.createClient(app.bearerOnly('revokeapp'), realmName); + const app = new NodeApp() + const client = admin.createClient(app.bearerOnly('revokeapp'), realmName) return client.then((installation) => { - app.build(installation); + app.build(installation) return getToken().then((token) => { const opt = { @@ -148,42 +148,42 @@ test('Should test protected route after push revocation.', t => { Authorization: 'Bearer ' + token, Accept: 'application/json' } - }; + } return axios(opt) .then(response => { - t.equal(response.data.message, 'admin'); + t.equal(response.data.message, 'admin') - opt.url = `${app.address}/admin/realms/${realmName}/push-revocation`; - opt.method = 'post'; - axios(opt); - opt.url = `${app.address}/service/admin`; + opt.url = `${app.address}/admin/realms/${realmName}/push-revocation` + opt.method = 'post' + axios(opt) + opt.url = `${app.address}/service/admin` return axios(opt) .then(response => { - t.equal(response.data, 'Not found!'); + t.equal(response.data, 'Not found!') }) .catch(error => { - t.fail(error.response.data); - }); - }); - }); + t.fail(error.response.data) + }) + }) + }) }).then(() => { - app.destroy(); + app.destroy() }) .catch(err => { - app.destroy(); - throw err; - }); -}); + app.destroy() + throw err + }) +}) test('Should invoke admin logout.', t => { - t.plan(2); + t.plan(2) - const app = new NodeApp(); - const client = admin.createClient(app.bearerOnly('anotherapp'), realmName); + const app = new NodeApp() + const client = admin.createClient(app.bearerOnly('anotherapp'), realmName) return client.then((installation) => { - app.build(installation); + app.build(installation) return getToken().then((token) => { const opt = { @@ -193,37 +193,37 @@ test('Should invoke admin logout.', t => { Authorization: 'Bearer ' + token, Accept: 'application/json' } - }; + } return axios(opt) .then(response => { - t.equal(response.data.message, 'admin'); + t.equal(response.data.message, 'admin') - opt.url = `${app.address}/admin/realms/${realmName}/logout-all`; - opt.method = 'post'; - axios(opt); - opt.url = `${app.address}/service/admin`; + opt.url = `${app.address}/admin/realms/${realmName}/logout-all` + opt.method = 'post' + axios(opt) + opt.url = `${app.address}/service/admin` return axios(opt) .then(response => { - t.equal(response.data, 'Not found!'); + t.equal(response.data, 'Not found!') }) .catch(error => { - t.fail(error.response.data); - }); - }); - }); + t.fail(error.response.data) + }) + }) + }) }).then(() => { - app.destroy(); + app.destroy() }) .catch(err => { - app.destroy(); - throw err; - }); -}); + app.destroy() + throw err + }) +}) test('teardown', t => { return realmManager.then((realm) => { - app.destroy(); - admin.destroy(realmName); - }); -}); + app.destroy() + admin.destroy(realmName) + }) +}) diff --git a/test/keycloak-connect-web-enforcer-spec.js b/test/keycloak-connect-web-enforcer-spec.js index 99924eef..f826b486 100644 --- a/test/keycloak-connect-web-enforcer-spec.js +++ b/test/keycloak-connect-web-enforcer-spec.js @@ -13,50 +13,50 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const test = require('blue-tape'); -const admin = require('./utils/realm'); +const test = require('blue-tape') +const admin = require('./utils/realm') -const page = require('./utils/webdriver').newPage; -const NodeApp = require('./fixtures/node-console/index').NodeApp; +const page = require('./utils/webdriver').newPage +const NodeApp = require('./fixtures/node-console/index').NodeApp -const realmManager = admin.createRealm(); -const app = new NodeApp(); +const realmManager = admin.createRealm() +const app = new NodeApp() test('setup', t => { return realmManager.then(() => { return admin.createClient(app.enforcerResourceServer()) .then((installation) => { - return app.build(installation); - }); - }); -}); + return app.build(installation) + }) + }) +}) test('Should be able to access resource protected by the policy enforcer', t => { - t.plan(3); + t.plan(3) - page.get(app.port); + page.get(app.port) return page.output().getText().then(text => { - t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated'); - page.logInButton().click(); - page.login('test-admin', 'password'); + t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated') + page.logInButton().click() + page.login('test-admin', 'password') return page.events().getText().then(text => { - t.equal(text, 'Auth Success', 'User should be authenticated'); - page.grantedResourceButton().click(); + t.equal(text, 'Auth Success', 'User should be authenticated') + page.grantedResourceButton().click() return page.events().getText().then(text => { - t.equal(text, 'Granted', 'User can access resource protected by the policy enforcer'); - }); - }); - }); -}); + t.equal(text, 'Granted', 'User can access resource protected by the policy enforcer') + }) + }) + }) +}) test('teardown', t => { return realmManager.then((realm) => { - app.destroy(); - admin.destroy('test-realm'); - page.quit(); - }); -}); + app.destroy() + admin.destroy('test-realm') + page.quit() + }) +}) diff --git a/test/keycloak-connect-web-spec.js b/test/keycloak-connect-web-spec.js index 9e06540d..36987e40 100644 --- a/test/keycloak-connect-web-spec.js +++ b/test/keycloak-connect-web-spec.js @@ -13,29 +13,29 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' -const test = require('blue-tape'); -const admin = require('./utils/realm'); -const TestVector = require('./utils/helper').TestVector; +const test = require('blue-tape') +const admin = require('./utils/realm') +const TestVector = require('./utils/helper').TestVector -const page = require('./utils/webdriver').newPage; -const realmAccountPage = require('./utils/webdriver').realmAccountPage; -const driver = require('./utils/webdriver').driver; -const NodeApp = require('./fixtures/node-console/index').NodeApp; -const session = require('express-session'); +const page = require('./utils/webdriver').newPage +const realmAccountPage = require('./utils/webdriver').realmAccountPage +const driver = require('./utils/webdriver').driver +const NodeApp = require('./fixtures/node-console/index').NodeApp +const session = require('express-session') -const realmManager = admin.createRealm(); -const app = new NodeApp(); +const realmManager = admin.createRealm() +const app = new NodeApp() test('setup', t => { return realmManager.then(() => { return admin.createClient(app.publicClient()) .then((installation) => { - return app.build(installation); - }); - }); -}); + return app.build(installation) + }) + }) +}) // test('setup', t => { // return client = realmManager.then((realm) => { @@ -44,276 +44,276 @@ test('setup', t => { // }); test('Should be able to access public page', t => { - t.plan(1); + t.plan(1) return page.get(app.port) .then(() => page.output().getText() .then(text => { - t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated'); + t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated') }) - ); -}); + ) +}) test('Should login with admin credentials', t => { - t.plan(3); + t.plan(3) return page.get(app.port) .then(() => page.output().getText() .then(text => { - t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated'); + t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated') return page.logInButton() .then(webElement => webElement.click()) .then(() => page.login('test-admin', 'password')) .then(() => page.events().getText().then(text => { - t.equal(text, 'Auth Success', 'User should be authenticated'); + t.equal(text, 'Auth Success', 'User should be authenticated') return page.logOutButton() .then(webElement => webElement.click()) .then(() => page.output().getText() .then(text => { - t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated'); + t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated') }) - ); - })); + ) + })) }) - ); -}); + ) +}) test('Login should not change tokens when they are valid', t => { - t.plan(3); + t.plan(3) return page.get(app.port).then(() => page.logInButton().click().then(() => page.login('test-admin', 'password').then(() => page.events().getText().then(text => { - t.equal(text, 'Auth Success', 'User should be authenticated'); + t.equal(text, 'Auth Success', 'User should be authenticated') return page.output().getText().then(firstToken => page.logInButton().click().then( // Invoke login for the second time, token shouldn't be changed page.output().getText().then(secondToken => { - t.equal(secondToken, firstToken, 'Token should not be changed as first session is still valid'); - page.logOutButton().click(); + t.equal(secondToken, firstToken, 'Token should not be changed as first session is still valid') + page.logOutButton().click() return page.output().getText().then(text => { - t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated'); - }); + t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated') + }) }) ) - ); + ) }) ) ) - ); -}); + ) +}) test('SSO should work for nodejs app and testRealmAccountPage', t => { return page.get(app.port).then(() => page.logInButton().click().then(() => page.login('test-admin', 'password').then(() => page.events().getText().then(text => { - t.equal(text, 'Auth Success', 'User should be authenticated'); + t.equal(text, 'Auth Success', 'User should be authenticated') return realmAccountPage.get().then(() => driver.getCurrentUrl().then(currentUrl => { - t.equal(currentUrl, realmAccountPage.getUrl(), 'Should be on account page'); + t.equal(currentUrl, realmAccountPage.getUrl(), 'Should be on account page') return realmAccountPage.logout().then(() => driver.getCurrentUrl().then(currentUrl => { - t.true(currentUrl.startsWith('http://127.0.0.1:8080/realms/test-realm/protocol/openid-connect/auth'), 'Should be on login page after AccountPage logout, current url: ' + currentUrl); + t.true(currentUrl.startsWith('http://127.0.0.1:8080/realms/test-realm/protocol/openid-connect/auth'), 'Should be on login page after AccountPage logout, current url: ' + currentUrl) return page.get(app.port, '/login').then(() => t.true(currentUrl.startsWith('http://127.0.0.1:8080/realms/test-realm/protocol/openid-connect/auth'), 'Should be on login page, current url: ' + currentUrl) - ); + ) }) - ); + ) }) - ); + ) }) ) ) - ); -}); + ) +}) test('Public client should be redirected to GitHub when idpHint is provided', t => { - t.plan(1); - const app = new NodeApp(); - const client = admin.createClient(app.publicClient('appIdP')); + t.plan(1) + const app = new NodeApp() + const client = admin.createClient(app.publicClient('appIdP')) return client.then((installation) => { - app.build(installation, { store: new session.MemoryStore(), idpHint: 'github' }); + app.build(installation, { store: new session.MemoryStore(), idpHint: 'github' }) return page.get(app.port, '/restricted') .then(() => page.h1().getText().then(text => { - t.equal(text, 'Sign in to GitHub', 'Application should redirect to GitHub'); + t.equal(text, 'Sign in to GitHub', 'Application should redirect to GitHub') }) ).then(() => { - app.destroy(); + app.destroy() }).catch(err => { - app.destroy(); - throw err; - }); - }); -}); + app.destroy() + throw err + }) + }) +}) test('User should be forbidden to access restricted page', t => { return page.get(app.port, '/restricted').then(() => page.login('alice', 'password').then(() => page.body().getText().then(text => { - t.equal(text, 'Access denied', 'Message should be access denied'); - return page.logout(app.port); // we need to wait a bit until the logout is fully completed + t.equal(text, 'Access denied', 'Message should be access denied') + return page.logout(app.port) // we need to wait a bit until the logout is fully completed }) ) - ); -}); + ) +}) test('Public client should be forbidden for invalid public key', t => { - t.plan(2); - const app = new NodeApp(); - const client = admin.createClient(app.publicClient('app2')); + t.plan(2) + const app = new NodeApp() + const client = admin.createClient(app.publicClient('app2')) return client.then((installation) => { - installation['realm-public-key'] = TestVector.wrongRealmPublicKey; - app.build(installation); + installation['realm-public-key'] = TestVector.wrongRealmPublicKey + app.build(installation) return page.get(app.port).then(() => page.output().getText().then(text => { - t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated'); + t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated') return page.logInButton().click().then(() => page.login('test-admin', 'password').then(() => page.body().getText().then(text => { - t.equal(text, 'Access denied', 'Message should be access denied'); + t.equal(text, 'Access denied', 'Message should be access denied') }) ) - ); + ) }) ).then(() => { - app.destroy(); + app.destroy() }).catch(err => { - app.destroy(); - throw err; - }); - }); -}); + app.destroy() + throw err + }) + }) +}) test('Confidential client should be forbidden for invalid public key', t => { - t.plan(3); - const app = new NodeApp(); - const client = admin.createClient(app.confidential('app3')); + t.plan(3) + const app = new NodeApp() + const client = admin.createClient(app.confidential('app3')) return client.then((installation) => { - installation['realm-public-key'] = TestVector.wrongRealmPublicKey; - app.build(installation); + installation['realm-public-key'] = TestVector.wrongRealmPublicKey + app.build(installation) return page.get(app.port).then(() => page.output().getText().then(text => { - t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated'); + t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated') return page.logInButton().click().then(() => page.body().getText().then(text => { - t.equal(text, 'Access denied', 'Message should be access denied'); + t.equal(text, 'Access denied', 'Message should be access denied') }) .then(() => page.logout(app.port)) .then(() => page.logoutConfirm()) .then(() => page.get(app.port, '/check-sso')) .then(() => page.output().getText().then(text => t.equal(text, 'Check SSO Success (Not Authenticated)', 'User should not be authenticated'))) - ); + ) }) ).then(() => { - app.destroy(); + app.destroy() }).catch(err => { - app.destroy(); - throw err; - }); - }); -}); + app.destroy() + throw err + }) + }) +}) test('Should test check SSO after logging in and logging out', t => { - t.plan(3); + t.plan(3) // make sure user is logged out page.get(app.port, '/check-sso').then(() => page.output().getText().then(text => { - t.equal(text, 'Check SSO Success (Not Authenticated)', 'User should not be authenticated'); + t.equal(text, 'Check SSO Success (Not Authenticated)', 'User should not be authenticated') page.logInButton().click().then(() => page.login('alice', 'password').then(() => page.get(app.port, '/check-sso').then(() => page.output().getText().then(text => { - t.equal(text, 'Check SSO Success (Authenticated)', 'User should be authenticated'); - return page.logout(app.port); + t.equal(text, 'Check SSO Success (Authenticated)', 'User should be authenticated') + return page.logout(app.port) }).then(() => { page.get(app.port, '/check-sso').then(() => page.output().getText().then(text => { - t.equal(text, 'Check SSO Success (Not Authenticated)', 'User should not be authenticated'); + t.equal(text, 'Check SSO Success (Not Authenticated)', 'User should not be authenticated') }) - ); + ) }) ) ) - ); + ) }) - ); -}); + ) +}) test('Public client should work with slash in the end of auth-server-url', t => { - t.plan(3); - const app = new NodeApp(); - const client = admin.createClient(app.publicClient('authServerSlashes')); + t.plan(3) + const app = new NodeApp() + const client = admin.createClient(app.publicClient('authServerSlashes')) return client.then((installation) => { - installation['auth-server-url'] = 'http://localhost:8080/'; - app.build(installation); + installation['auth-server-url'] = 'http://localhost:8080/' + app.build(installation) return page.get(app.port) .then(() => page.output().getText() .then(text => { - t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated'); + t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated') return page.logInButton() .then(webElement => webElement.click()) .then(() => page.login('test-admin', 'password')) .then(() => page.events().getText().then(text => { - t.equal(text, 'Auth Success', 'User should be authenticated'); + t.equal(text, 'Auth Success', 'User should be authenticated') return page.logOutButton() .then(webElement => webElement.click()) .then(() => page.output().getText() .then(text => { - t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated'); + t.equal(text, 'Init Success (Not Authenticated)', 'User should not be authenticated') }) - ); - })); + ) + })) }) ).then(() => { - app.destroy(); + app.destroy() }).catch(err => { - app.destroy(); - throw err; - }); - }); -}); + app.destroy() + throw err + }) + }) +}) test('App should be able to use cookie-store', t => { - t.plan(1); - const app = new NodeApp(); - const client = admin.createClient(app.publicClient('appCookies')); + t.plan(1) + const app = new NodeApp() + const client = admin.createClient(app.publicClient('appCookies')) return client.then((installation) => { - app.build(installation, { cookies: true }); + app.build(installation, { cookies: true }) return page.get(app.port, '/cookie').then(() => { page.login('alice', 'password').then(() => { driver.navigate().refresh().then(() => { page.events().getText().then(text => { - t.equal(text, 'Auth Success', 'User should be authenticated'); - }); - }); - }); + t.equal(text, 'Auth Success', 'User should be authenticated') + }) + }) + }) }).then(() => { - app.destroy(); + app.destroy() }).catch(err => { - app.destroy(); - throw err; - }); - }); -}); + app.destroy() + throw err + }) + }) +}) test('teardown', t => { return realmManager.then((realm) => { - app.destroy(); - admin.destroy('test-realm'); - page.quit(); - }); -}); + app.destroy() + admin.destroy('test-realm') + page.quit() + }) +}) diff --git a/test/unit/hconfig-test.js b/test/unit/hconfig-test.js index 0ee4a804..089d2902 100644 --- a/test/unit/hconfig-test.js +++ b/test/unit/hconfig-test.js @@ -1,73 +1,73 @@ -'use strict'; +'use strict' -const test = require('tape-catch'); -const RSA = require('rsa-compat').RSA; -const Config = require('../../middleware/auth-utils/config'); +const test = require('tape-catch') +const RSA = require('rsa-compat').RSA +const Config = require('../../middleware/auth-utils/config') test('Config#configure', (t) => { - const cfg = new Config({ realm: 'test-realm' }); + const cfg = new Config({ realm: 'test-realm' }) - t.equal(cfg.realm, 'test-realm'); - t.end(); -}); + t.equal(cfg.realm, 'test-realm') + t.end() +}) test('Config#configure with boolean', (t) => { - const cfg = new Config({ public: true }); + const cfg = new Config({ public: true }) - t.equal(cfg.public, true); - t.end(); -}); + t.equal(cfg.public, true) + t.end() +}) /* eslint-disable no-template-curly-in-string */ test('Config#configure with env variable reference not set', (t) => { - const cfg = new Config({ realm: '${env.NOT_SET}' }); + const cfg = new Config({ realm: '${env.NOT_SET}' }) - t.equal(cfg.realm, ''); - t.end(); -}); + t.equal(cfg.realm, '') + t.end() +}) test('Config#configure with env variable reference not set with fallback', (t) => { - const cfg = new Config({ realm: '${env.NOT_SET:fallback}' }); + const cfg = new Config({ realm: '${env.NOT_SET:fallback}' }) - t.equal(cfg.realm, 'fallback'); - t.end(); -}); + t.equal(cfg.realm, 'fallback') + t.end() +}) test('Config#configure with env variable reference set', (t) => { - const cfg = new Config({ realm: '${env.USER}' }); + const cfg = new Config({ realm: '${env.USER}' }) - t.equal(cfg.realm, process.env.USER); - t.end(); -}); + t.equal(cfg.realm, process.env.USER) + t.end() +}) test('Config#configure with env variable reference set with fallback', (t) => { - const cfg = new Config({ realm: '${env.USER:fallback}' }); + const cfg = new Config({ realm: '${env.USER:fallback}' }) - t.equal(cfg.realm, process.env.USER); - t.end(); -}); + t.equal(cfg.realm, process.env.USER) + t.end() +}) test('Config#configure with realm-public-key', (t) => { - t.plan(2); + t.plan(2) RSA.generateKeypair(2048, 65537, { public: true, pem: true }, (err, keyz) => { - t.error(err, 'generated keypair successfully'); - const plainKey = keyz.publicKeyPem.split(/\r?\n/).filter(item => item && !item.startsWith('---')).join(''); - const cfg = new Config({ 'realm-public-key': plainKey }); + t.error(err, 'generated keypair successfully') + const plainKey = keyz.publicKeyPem.split(/\r?\n/).filter(item => item && !item.startsWith('---')).join('') + const cfg = new Config({ 'realm-public-key': plainKey }) // Added this due to the upgrades in rsa-compat headers - t.equal(cfg.publicKey, keyz.publicKeyPem.replace(/RSA PUBLIC/g, 'PUBLIC').replace(/\r/g, '')); - t.end(); - }); -}); + t.equal(cfg.publicKey, keyz.publicKeyPem.replace(/RSA PUBLIC/g, 'PUBLIC').replace(/\r/g, '')) + t.end() + }) +}) test('Config#configure with realmPublicKey', (t) => { - t.plan(2); + t.plan(2) RSA.generateKeypair(2048, 65537, { public: true, pem: true }, (err, keyz) => { - t.error(err, 'generated keypair successfully'); - const plainKey = keyz.publicKeyPem.split(/\r?\n/).filter(item => item && !item.startsWith('---')).join(''); - const cfg = new Config({ realmPublicKey: plainKey }); + t.error(err, 'generated keypair successfully') + const plainKey = keyz.publicKeyPem.split(/\r?\n/).filter(item => item && !item.startsWith('---')).join('') + const cfg = new Config({ realmPublicKey: plainKey }) // Added this due to the upgrades in rsa-compat headers - t.equal(cfg.publicKey, keyz.publicKeyPem.replace(/RSA PUBLIC/g, 'PUBLIC').replace(/\r/g, '')); - t.end(); - }); -}); + t.equal(cfg.publicKey, keyz.publicKeyPem.replace(/RSA PUBLIC/g, 'PUBLIC').replace(/\r/g, '')) + t.end() + }) +}) diff --git a/test/unit/keycloak-object-test.js b/test/unit/keycloak-object-test.js index 4d46f1cf..eb5b3cc2 100644 --- a/test/unit/keycloak-object-test.js +++ b/test/unit/keycloak-object-test.js @@ -14,22 +14,22 @@ * the License. */ -'use strict'; +'use strict' -const test = require('tape'); -const Keycloak = require('../../'); -const UUID = require('../../uuid'); -const session = require('express-session'); +const test = require('tape') +const Keycloak = require('../../') +const UUID = require('../../uuid') +const session = require('express-session') -let kc = null; +let kc = null test('Should raise an error when no configuration is provided.', t => { t.throws(function () { - const k = new Keycloak(); - t.notOk(k, 'Variable should be empty'); - }, Error, 'Adapter configuration must be provided.'); - t.end(); -}); + const k = new Keycloak() + t.notOk(k, 'Variable should be empty') + }, Error, 'Adapter configuration must be provided.') + t.end() +}) test('setup', t => { const kcConfig = { @@ -38,45 +38,45 @@ test('setup', t => { 'ssl-required': 'external', resource: 'nodejs-connect', 'public-client': true - }; + } - const memoryStore = new session.MemoryStore(); - kc = new Keycloak({ store: memoryStore, scope: 'offline_support' }, kcConfig); - t.end(); -}); + const memoryStore = new session.MemoryStore() + kc = new Keycloak({ store: memoryStore, scope: 'offline_support' }, kcConfig) + t.end() +}) test('Should verify the realm name of the config object.', t => { - t.equal(kc.config.realm, 'test-realm'); - t.end(); -}); + t.equal(kc.config.realm, 'test-realm') + t.end() +}) test('Should verify if login URL has the configured realm.', t => { - t.equal(kc.loginUrl().indexOf(kc.config.realm) > 0, true); - t.end(); -}); + t.equal(kc.loginUrl().indexOf(kc.config.realm) > 0, true) + t.end() +}) test('Should verify if login URL has the custom scope value.', t => { - t.equal(kc.loginUrl().indexOf(kc.config.scope) > 0, true); - t.end(); -}); + t.equal(kc.loginUrl().indexOf(kc.config.scope) > 0, true) + t.end() +}) test('Should verify if login URL has the default scope value.', t => { - t.equal(kc.loginUrl().indexOf('openid') > 0, true); - t.end(); -}); + t.equal(kc.loginUrl().indexOf('openid') > 0, true) + t.end() +}) test('Should verify if logout URL has the configured realm.', t => { - t.equal(kc.logoutUrl().indexOf(kc.config.realm) > 0, true); - t.end(); -}); + t.equal(kc.logoutUrl().indexOf(kc.config.realm) > 0, true) + t.end() +}) test('Should generate a correct UUID.', t => { - const rgx = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/; - t.equal(rgx.test(UUID()), true); - t.end(); -}); + const rgx = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/ + t.equal(rgx.test(UUID()), true) + t.end() +}) test('Should produce correct account url.', t => { - t.equal(kc.accountUrl(), 'http://localhost:8080/realms/test-realm/account'); - t.end(); -}); + t.equal(kc.accountUrl(), 'http://localhost:8080/realms/test-realm/account') + t.end() +}) diff --git a/test/utils/config.js b/test/utils/config.js index 9d0b50a7..a4854b9e 100644 --- a/test/utils/config.js +++ b/test/utils/config.js @@ -4,7 +4,7 @@ * @param {object} username - Username to any user with credentials to create realms * @param {object} password - password */ -'use strict'; +'use strict' const settings = { baseUrl: 'http://127.0.0.1:8080', @@ -13,6 +13,6 @@ const settings = { password: 'admin', grantType: 'password', clientId: 'admin-cli' -}; +} -module.exports = settings; +module.exports = settings diff --git a/test/utils/helper.js b/test/utils/helper.js index 737c343d..ebb9993b 100644 --- a/test/utils/helper.js +++ b/test/utils/helper.js @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' /** * A helper for test cases and fixtures */ -const fs = require('fs'); +const fs = require('fs') /** * Utility to parse realm templates @@ -30,17 +30,17 @@ const fs = require('fs'); function parse (file, realmName) { const content = fs.readFileSync(file, 'utf8') - .replace(/{{realm}}/g, realmName); - return JSON.parse(content); + .replace(/{{realm}}/g, realmName) + return JSON.parse(content) } function parseClient (file, httpPort, name) { - const port = httpPort || '3000'; + const port = httpPort || '3000' const content = fs.readFileSync(file, 'utf8') .replace(/{{name}}/g, name) - .replace(/{{port}}/g, port); - const json = JSON.parse(content); - return json; + .replace(/{{port}}/g, port) + const json = JSON.parse(content) + return json } /** @@ -50,18 +50,18 @@ function parseClient (file, httpPort, name) { function TestVector () { } -TestVector.wrongRealmPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAikGBvYGniAJ59ZjpaSDw2o+j40Ila/dWfN8qA1dzXJesH9Z1sZrcevJB+rfxoZDaWMz2l9Q3OxG/qolTpsQl8NBdb5tymic9qDkAIsiyKThzjcfs5lOSxfnkHn6+Z0QbrYnXQs/cGvQ1Ai81M1M1O6BHDWu05n8c977h+BsfLmqGj7MZZj9gw9RM84RIKDGHTbFh9YyXBJVtqbOhRD7hcB0O9olDZb7mQ5A8gsMctcUhsVBy3xKCLMD41XU92rQ9FAlsV9mBglLqaVWr2mxQItN3lgjE02L8UyorI3T0uprIsnv7B2NwUC5ZhwZGfnBznUPVrT6makEJklpg5if3qQIDAQAB'; -TestVector.logoutValidPayload = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkZKODZHY0YzalRiTkxPY280TnZaa1VDSVVtZllDcW9xdE9RZU1mYmhObEUifQ.eyJpYXQiOjE1NTY2MTcwNzksInJlc291cmNlIjoiYWRtaW5hcHAzIiwiYWN0aW9uIjoiTE9HT1VUIiwibm90QmVmb3JlIjoxNTg3MDQ3NTM3fQ.NplTHo8JuwtmUbpp3AHjM3c6rn7g_xGWegC-b8Gg7V2QoN9vPRb9oCc9fdD7qWKpXLgfNtvtTIJnIIP5O_ux7Jt_SQyNtwoPmf5k_EFmm7JSxPnVfVA36BJbGDJu_BiNbktGgpNVZR5HnAkawhsLLo05S0edFfnbs4N9a_4W_YM'; -TestVector.logoutWrongKeyPairPayload = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkZKODZHY0YzalRiTkxPY280TnZaa1VDSVVtZllDcW9xdE9RZU1mYmhObEUifQ.eyJpYXQiOjE1NTc4NjE2NjgsInJlc291cmNlIjoiYWRtaW5hcHAyIiwiYWN0aW9uIjoiTE9HT1VUIiwibm90QmVmb3JlIjoxNTg3MDQ3NTM3fQ.aqNUXVbf2poQBj9V2oHTUiEn3NrUAbpVBt_70MC-l2_ihwaer8c93KhB3VDFZVVHDf_Jq-9_JVvwV755LXbtNOLXvptTXBQYyXFeu1LfwJTON217xzNlf0izm2tdl5qDyjcYNNX1TrltlraZIh2j96BsgDCRx8k_m2c_H_4xCfoU73eqehID5ob1wNXtT8372Xiykzrwotpe9oXPhSEHRT7r62IvqfiYMJ7iTPaffGz9_oeeMTlOrx9YB29M7Y5KHPjYKjRPR8caNFYCI9j1HoQiMKNkcn5oTH7aUUnNE8S8x-YIlxeXLP1SqVrB2Psf2PXbTsMDr4R4JaJikwn1wA'; -TestVector.logoutIncompletePayload = '.eyJyZXNvdXJjZSI6Im5vZGVqcy1jb25uZWN0IiwiYWN0aW9uIjoiTE9HT1VUIiwibm90QmVmb3JlIjoxNTg3MDQ3NTM3fQ.'; -TestVector.notBeforeValidPayload = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkZKODZHY0YzalRiTkxPY280TnZaa1VDSVVtZllDcW9xdE9RZU1mYmhObEUifQ.eyJpYXQiOjE1NTY2MTgzNTAsInJlc291cmNlIjoiYWRtaW5hcHA3IiwiYWN0aW9uIjoiUFVTSF9OT1RfQkVGT1JFIiwibm90QmVmb3JlIjoxNTg3MDQ3NTM3fQ.iLrZ6Z2FXZt3XuTbRWXJzmK281p33Py_hhkcevPyVsW3OhOli5CZsEZBSKXnUBOJgEN9HQXRoRUr4KWhbquoQi_wUIjp0Cog_0qC8JepCvz0FWhaProgtJxKjlYgiY3kzjMI4MDFfeTE2xrcXzJ5qbYxmhU1t07_7t4BHzm7C6o'; -TestVector.notBeforeWrongKeyPairPayload = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkZKODZHY0YzalRiTkxPY280TnZaa1VDSVVtZllDcW9xdE9RZU1mYmhObEUifQ.eyJpYXQiOjE1NTY2MTg2NjIsInJlc291cmNlIjoiYWRtaW5hcHA2IiwiYWN0aW9uIjoiUFVTSF9OT1RfQkVGT1JFIiwibm90QmVmb3JlIjoxNTg3MDQ3NTM3fQ.X0EoW-9N_6jOn9VFkm3HxTwZS2cCm0ChCH3ddYcAnVcugGSrvv1K5vQy9czlalvEnLZ_HpaWNWoBYA7hoqR5S600A-BSMHrb6oPt2B1JW8htgubD8NbJC2COsOGAbxLupO9YEP_oodzpAF5ikMB3Pm2g1e66BFvotSQHAtgg7HepzywvPrkYork44worrX2ByHVK4Y5Or6BWleEx1pa59dqmZNfupaL4pKSG9j7H9NM1YmEuKwjHr9PIyN7bPkx64LamI5aUIk5rjIM8plnxiayEgdCr9B6ag0xVoKggv3GV0m-XsRkbUPl91EbLQXwSCYdL5TQsvK5uJqkba9eiRA'; -TestVector.notBeforeIncompletePayload = '.eyJyZXNvdXJjZSI6ImFkbWluYXBwNSIsImFjdGlvbiI6IlBVU0hfTk9UX0JFRk9SRSIsIm5vdEJlZm9yZSI6MTU4NzA0NzUzN30.'; +TestVector.wrongRealmPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAikGBvYGniAJ59ZjpaSDw2o+j40Ila/dWfN8qA1dzXJesH9Z1sZrcevJB+rfxoZDaWMz2l9Q3OxG/qolTpsQl8NBdb5tymic9qDkAIsiyKThzjcfs5lOSxfnkHn6+Z0QbrYnXQs/cGvQ1Ai81M1M1O6BHDWu05n8c977h+BsfLmqGj7MZZj9gw9RM84RIKDGHTbFh9YyXBJVtqbOhRD7hcB0O9olDZb7mQ5A8gsMctcUhsVBy3xKCLMD41XU92rQ9FAlsV9mBglLqaVWr2mxQItN3lgjE02L8UyorI3T0uprIsnv7B2NwUC5ZhwZGfnBznUPVrT6makEJklpg5if3qQIDAQAB' +TestVector.logoutValidPayload = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkZKODZHY0YzalRiTkxPY280TnZaa1VDSVVtZllDcW9xdE9RZU1mYmhObEUifQ.eyJpYXQiOjE1NTY2MTcwNzksInJlc291cmNlIjoiYWRtaW5hcHAzIiwiYWN0aW9uIjoiTE9HT1VUIiwibm90QmVmb3JlIjoxNTg3MDQ3NTM3fQ.NplTHo8JuwtmUbpp3AHjM3c6rn7g_xGWegC-b8Gg7V2QoN9vPRb9oCc9fdD7qWKpXLgfNtvtTIJnIIP5O_ux7Jt_SQyNtwoPmf5k_EFmm7JSxPnVfVA36BJbGDJu_BiNbktGgpNVZR5HnAkawhsLLo05S0edFfnbs4N9a_4W_YM' +TestVector.logoutWrongKeyPairPayload = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkZKODZHY0YzalRiTkxPY280TnZaa1VDSVVtZllDcW9xdE9RZU1mYmhObEUifQ.eyJpYXQiOjE1NTc4NjE2NjgsInJlc291cmNlIjoiYWRtaW5hcHAyIiwiYWN0aW9uIjoiTE9HT1VUIiwibm90QmVmb3JlIjoxNTg3MDQ3NTM3fQ.aqNUXVbf2poQBj9V2oHTUiEn3NrUAbpVBt_70MC-l2_ihwaer8c93KhB3VDFZVVHDf_Jq-9_JVvwV755LXbtNOLXvptTXBQYyXFeu1LfwJTON217xzNlf0izm2tdl5qDyjcYNNX1TrltlraZIh2j96BsgDCRx8k_m2c_H_4xCfoU73eqehID5ob1wNXtT8372Xiykzrwotpe9oXPhSEHRT7r62IvqfiYMJ7iTPaffGz9_oeeMTlOrx9YB29M7Y5KHPjYKjRPR8caNFYCI9j1HoQiMKNkcn5oTH7aUUnNE8S8x-YIlxeXLP1SqVrB2Psf2PXbTsMDr4R4JaJikwn1wA' +TestVector.logoutIncompletePayload = '.eyJyZXNvdXJjZSI6Im5vZGVqcy1jb25uZWN0IiwiYWN0aW9uIjoiTE9HT1VUIiwibm90QmVmb3JlIjoxNTg3MDQ3NTM3fQ.' +TestVector.notBeforeValidPayload = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkZKODZHY0YzalRiTkxPY280TnZaa1VDSVVtZllDcW9xdE9RZU1mYmhObEUifQ.eyJpYXQiOjE1NTY2MTgzNTAsInJlc291cmNlIjoiYWRtaW5hcHA3IiwiYWN0aW9uIjoiUFVTSF9OT1RfQkVGT1JFIiwibm90QmVmb3JlIjoxNTg3MDQ3NTM3fQ.iLrZ6Z2FXZt3XuTbRWXJzmK281p33Py_hhkcevPyVsW3OhOli5CZsEZBSKXnUBOJgEN9HQXRoRUr4KWhbquoQi_wUIjp0Cog_0qC8JepCvz0FWhaProgtJxKjlYgiY3kzjMI4MDFfeTE2xrcXzJ5qbYxmhU1t07_7t4BHzm7C6o' +TestVector.notBeforeWrongKeyPairPayload = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkZKODZHY0YzalRiTkxPY280TnZaa1VDSVVtZllDcW9xdE9RZU1mYmhObEUifQ.eyJpYXQiOjE1NTY2MTg2NjIsInJlc291cmNlIjoiYWRtaW5hcHA2IiwiYWN0aW9uIjoiUFVTSF9OT1RfQkVGT1JFIiwibm90QmVmb3JlIjoxNTg3MDQ3NTM3fQ.X0EoW-9N_6jOn9VFkm3HxTwZS2cCm0ChCH3ddYcAnVcugGSrvv1K5vQy9czlalvEnLZ_HpaWNWoBYA7hoqR5S600A-BSMHrb6oPt2B1JW8htgubD8NbJC2COsOGAbxLupO9YEP_oodzpAF5ikMB3Pm2g1e66BFvotSQHAtgg7HepzywvPrkYork44worrX2ByHVK4Y5Or6BWleEx1pa59dqmZNfupaL4pKSG9j7H9NM1YmEuKwjHr9PIyN7bPkx64LamI5aUIk5rjIM8plnxiayEgdCr9B6ag0xVoKggv3GV0m-XsRkbUPl91EbLQXwSCYdL5TQsvK5uJqkba9eiRA' +TestVector.notBeforeIncompletePayload = '.eyJyZXNvdXJjZSI6ImFkbWluYXBwNSIsImFjdGlvbiI6IlBVU0hfTk9UX0JFRk9SRSIsIm5vdEJlZm9yZSI6MTU4NzA0NzUzN30.' module.exports = { - parse: parse, - TestVector: TestVector, - parseClient: parseClient, + parse, + TestVector, + parseClient, dummyReply: { access_token: 'Dummy access token', expires_in: 2, @@ -72,4 +72,4 @@ module.exports = { 'not-before-policy': 1462208947, session_state: '22e0b5bd-fb0f-4f99-93aa-a60c4b934c88' } -}; +} diff --git a/test/utils/realm.js b/test/utils/realm.js index 52919ce6..8eb84cc8 100644 --- a/test/utils/realm.js +++ b/test/utils/realm.js @@ -13,17 +13,17 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' /** * A wrapper to keycloak-admin-client with an initial setup */ /* eslint new-cap: ["error", { "newIsCap": false }] */ -const keycloakAdminClient = require('@keycloak/keycloak-admin-client'); -const parse = require('./helper').parse; -const settings = require('./config'); -const realmTemplate = 'test/fixtures/testrealm.json'; +const keycloakAdminClient = require('@keycloak/keycloak-admin-client') +const parse = require('./helper').parse +const settings = require('./config') +const realmTemplate = 'test/fixtures/testrealm.json' -const kca = new keycloakAdminClient.default(settings); +const kca = new keycloakAdminClient.default(settings) /** * Create realms based on port and name specified @@ -33,14 +33,14 @@ const kca = new keycloakAdminClient.default(settings); * @returns {Promise} A promise that will resolve with the realm object. */ function createRealm (realmName) { - const name = realmName || 'test-realm'; + const name = realmName || 'test-realm' return kca.auth(settings).then(() => { return kca.realms.create(parse(realmTemplate, name)).then(() => { - return kca.realms.findOne({ realm: realmName }); - }); + return kca.realms.findOne({ realm: realmName }) + }) }).catch((err) => { - console.error('Failure: ', err); - }); + console.error('Failure: ', err) + }) } /** @@ -50,32 +50,32 @@ function createRealm (realmName) { * @returns {Promise} A promise that will resolve with the realm object. */ function createClient (clientRep, realmName) { - const realm = realmName || 'test-realm'; - kca.setConfig({ realmName: 'master' }); + const realm = realmName || 'test-realm' + kca.setConfig({ realmName: 'master' }) return kca.auth(settings).then(() => { - kca.setConfig({ realmName: realm }); + kca.setConfig({ realmName: realm }) return kca.clients.create(clientRep).then((rep) => { - return kca.clients.getInstallationProviders({ id: rep.id, providerId: 'keycloak-oidc-keycloak-json' }); - }); + return kca.clients.getInstallationProviders({ id: rep.id, providerId: 'keycloak-oidc-keycloak-json' }) + }) }).catch(err => { - console.error(err); - }); + console.error(err) + }) } /** * Remove the realm based on the name provided * @param {object} realm - Realm name */ function destroy (realm) { - kca.setConfig({ realmName: 'master' }); + kca.setConfig({ realmName: 'master' }) kca.auth(settings).then(() => { - return kca.realms.del({ realm }); + return kca.realms.del({ realm }) }).catch((err) => { - console.error('Realm was not found to remove:', err); - }); + console.error('Realm was not found to remove:', err) + }) } module.exports = { - createRealm: createRealm, - createClient: createClient, - destroy: destroy -}; + createRealm, + createClient, + destroy +} diff --git a/test/utils/token.js b/test/utils/token.js index 278c6f04..d7077282 100644 --- a/test/utils/token.js +++ b/test/utils/token.js @@ -1,7 +1,7 @@ -'use strict'; +'use strict' -const requester = require('keycloak-request-token'); -const baseUrl = 'http://127.0.0.1:8080'; +const requester = require('keycloak-request-token') +const baseUrl = 'http://127.0.0.1:8080' const defaultSettings = { username: 'test-admin', @@ -9,9 +9,9 @@ const defaultSettings = { grant_type: 'password', client_id: 'admin-app', realmName: 'service-node-realm' -}; +} module.exports = (options) => { - const settings = Object.assign({}, defaultSettings, options); - return requester(baseUrl, settings); -}; + const settings = Object.assign({}, defaultSettings, options) + return requester(baseUrl, settings) +} diff --git a/test/utils/webdriver.js b/test/utils/webdriver.js index 595a33df..ee8cb6fa 100644 --- a/test/utils/webdriver.js +++ b/test/utils/webdriver.js @@ -13,122 +13,122 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' /** * An utility with the specifics for selenium */ -const chrome = require('selenium-webdriver/chrome'); -const webdriver = require('selenium-webdriver'); -const args = require('minimist')(process.argv.slice(2)); -const By = webdriver.By; -const until = webdriver.until; -const driver = createDriver(); +const chrome = require('selenium-webdriver/chrome') +const webdriver = require('selenium-webdriver') +const args = require('minimist')(process.argv.slice(2)) +const By = webdriver.By +const until = webdriver.until +const driver = createDriver() function createDriver () { - chrome.setDefaultService(new chrome.ServiceBuilder(determineChromedriverPath()).build()); + chrome.setDefaultService(new chrome.ServiceBuilder(determineChromedriverPath()).build()) - const o = new chrome.Options(); - o.addArguments('disable-infobars'); - o.addArguments('headless'); + const o = new chrome.Options() + o.addArguments('disable-infobars') + o.addArguments('headless') if (args.chromeArguments) { - const chromeArgs = args.chromeArguments.split(' '); - console.log('Using additional chrome arguments [' + chromeArgs + ']'); - o.addArguments(chromeArgs); + const chromeArgs = args.chromeArguments.split(' ') + console.log('Using additional chrome arguments [' + chromeArgs + ']') + o.addArguments(chromeArgs) } - o.setUserPreferences({ credential_enable_service: false }); + o.setUserPreferences({ credential_enable_service: false }) const driver = new webdriver.Builder() .setChromeOptions(o) .forBrowser('chrome') - .build(); + .build() driver.getCapabilities().then((caps) => { - console.log('Chrome browser version: ' + caps.get('version')); - console.log('Chromedriver version: ' + caps.get('chrome').chromedriverVersion); - }); + console.log('Chrome browser version: ' + caps.get('version')) + console.log('Chromedriver version: ' + caps.get('chrome').chromedriverVersion) + }) - return driver; + return driver } function determineChromedriverPath () { - let path = args.chromedriverPath || process.env.CHROMEDRIVER_PATH; + let path = args.chromedriverPath || process.env.CHROMEDRIVER_PATH if (!path) { - const chromedriver = require('chromedriver'); - path = chromedriver.path; + const chromedriver = require('chromedriver') + path = chromedriver.path } - console.log('Using chromedriver from path: ' + path); - return path; + console.log('Using chromedriver from path: ' + path) + return path } /* eslint-disable no-unused-vars */ function waitForElement (locator, t) { - const timeout = t || 3000; - return driver.wait(until.elementLocated(locator), timeout); + const timeout = t || 3000 + return driver.wait(until.elementLocated(locator), timeout) } /* eslint-disable no-unused-vars */ function waitForVisibleElement (locator, t) { - const timeout = t || 3000; - const element = driver.wait(until.elementLocated(locator), timeout); + const timeout = t || 3000 + const element = driver.wait(until.elementLocated(locator), timeout) return driver.wait(new webdriver.WebElementCondition('for element to be visible ' + locator, function () { - return element.isDisplayed().then(x => x ? element : null); - }), timeout); + return element.isDisplayed().then(x => x ? element : null) + }), timeout) } function ConsolePage () {} ConsolePage.prototype.get = function (port, resource) { - resource = resource || ''; - return driver.get(`http://localhost:${port}${resource}`); -}; + resource = resource || '' + return driver.get(`http://localhost:${port}${resource}`) +} ConsolePage.prototype.quit = function () { - driver.manage().deleteAllCookies(); - driver.quit(); -}; + driver.manage().deleteAllCookies() + driver.quit() +} ConsolePage.prototype.logInButton = function () { - return driver.findElement(By.xpath("//button[text() = 'Login']")); -}; + return driver.findElement(By.xpath("//button[text() = 'Login']")) +} ConsolePage.prototype.output = function () { - return driver.findElement(By.id('output')); -}; + return driver.findElement(By.id('output')) +} ConsolePage.prototype.logOutButton = () => { - return driver.findElement(By.xpath("//button[text() = 'Logout']")); -}; + return driver.findElement(By.xpath("//button[text() = 'Logout']")) +} ConsolePage.prototype.events = function () { - return driver.findElement(By.id('events')); -}; + return driver.findElement(By.id('events')) +} ConsolePage.prototype.print = function () { driver.getPageSource().then((page) => { - console.log(page); - }); -}; + console.log(page) + }) +} ConsolePage.prototype.grantedResourceButton = function () { - return driver.findElement(By.xpath("//button[text() = 'Granted Resource']")); -}; + return driver.findElement(By.xpath("//button[text() = 'Granted Resource']")) +} ConsolePage.prototype.login = function (user, pass) { - waitForVisibleElement(By.id('username'), 100000); - const username = driver.findElement(By.id('username')); - username.clear(); - username.sendKeys(user); + waitForVisibleElement(By.id('username'), 100000) + const username = driver.findElement(By.id('username')) + username.clear() + username.sendKeys(user) - const password = driver.findElement(By.id('password')); - password.clear(); - password.sendKeys(pass); + const password = driver.findElement(By.id('password')) + password.clear() + password.sendKeys(pass) - return driver.findElement(By.name('login')).then(webElement => webElement.click()); -}; + return driver.findElement(By.name('login')).then(webElement => webElement.click()) +} /** * Logouts directly with support for a wait period @@ -137,54 +137,54 @@ ConsolePage.prototype.login = function (user, pass) { * @returns {Promise} */ ConsolePage.prototype.logout = function (port) { - this.get(port, '/logout'); + this.get(port, '/logout') return new Promise((resolve, reject) => { setTimeout(() => { - resolve(); - }, 2000); - }); -}; + resolve() + }, 2000) + }) +} /** * Confirmation of the logout screen */ ConsolePage.prototype.logoutConfirm = function () { - waitForVisibleElement(By.id('kc-logout'), 100000); - return driver.findElement(By.id('kc-logout')).then(webElement => webElement.click()); -}; + waitForVisibleElement(By.id('kc-logout'), 100000) + return driver.findElement(By.id('kc-logout')).then(webElement => webElement.click()) +} ConsolePage.prototype.body = () => { - return driver.findElement(By.tagName('pre')); -}; + return driver.findElement(By.tagName('pre')) +} ConsolePage.prototype.h1 = () => { - return driver.findElement(By.tagName('h1')); -}; + return driver.findElement(By.tagName('h1')) +} -const newPage = new ConsolePage(); +const newPage = new ConsolePage() function RealmAccountPage () {} RealmAccountPage.prototype.get = function (port, realm) { - return driver.get(this.getUrl(port, realm)); -}; + return driver.get(this.getUrl(port, realm)) +} RealmAccountPage.prototype.getUrl = function (port, realm) { - port = port || '8080'; - realm = realm || 'test-realm'; + port = port || '8080' + realm = realm || 'test-realm' - return `http://127.0.0.1:${port}/realms/${realm}/account`; -}; + return `http://127.0.0.1:${port}/realms/${realm}/account` +} RealmAccountPage.prototype.logout = function () { - return driver.findElement(By.linkText('Sign out')).then(webElement => webElement.click()); -}; + return driver.findElement(By.linkText('Sign out')).then(webElement => webElement.click()) +} -const realmAccountPage = new RealmAccountPage(); +const realmAccountPage = new RealmAccountPage() module.exports = { - driver: driver, - webdriver: webdriver, - newPage: newPage, - realmAccountPage: realmAccountPage -}; + driver, + webdriver, + newPage, + realmAccountPage +} diff --git a/uuid.js b/uuid.js index 8cc96576..3794be1b 100644 --- a/uuid.js +++ b/uuid.js @@ -13,16 +13,16 @@ * License for the specific language governing permissions and limitations under * the License. */ -'use strict'; +'use strict' module.exports = function () { - const s = []; - const hexDigits = '0123456789abcdef'; + const s = [] + const hexDigits = '0123456789abcdef' for (let i = 0; i < 36; i++) { - s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); + s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1) } - s[14] = '4'; - s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); - s[8] = s[13] = s[18] = s[23] = '-'; - return s.join(''); -}; + s[14] = '4' + s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1) + s[8] = s[13] = s[18] = s[23] = '-' + return s.join('') +}