diff --git a/config/test.js b/config/test.js index f3aa3db6..32fb2aec 100644 --- a/config/test.js +++ b/config/test.js @@ -153,6 +153,28 @@ const passportConfigAuthorizedResponse = { clientSecret: 'Admin1Admin!', issuer: 'https://gluu.test.ce6.local.org' } + }, { + id: 'saml-redis-test', + displayName: 'saml redis', + type: 'saml', + mapping: 'saml_ldap_profile', + passportStrategyId: 'passport-saml', + enabled: true, + callbackUrl: + 'https://chris.gluuthree.org/passport/auth/saml' + + '/saml-redis-test/callback', + requestForEmail: false, + emailLinkingSafe: false, + options: { + skipRequestCompression: 'True', + authnRequestBinding: 'HTTP-POST', + identifierFormat: + 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient', + cert: 'MIIDlzCCAn8CFBgf85Th/k9LW/WX1Tm2K8L46XFKMA0GCSqGSIb3DQEBCwUAMIGHMQswCQYDVQQGEwJCUjELMAkGA1UECAwCU1AxEjAQBgNVBAcMCVNhbyBQYXVsbzEZMBcGA1UECgwQQ2hyaXMgVGVzdGluZyBuQzEaMBgGA1UEAwwRY2hyaXMuZ2x1dXR3by5vcmcxIDAeBgkqhkiG9w0BCQEWEWNocmlzQHRlc3RpbmcuY29tMB4XDTIwMDYyMzE0NDU1M1oXDTIxMDYyMzE0NDU1M1owgYcxCzAJBgNVBAYTAkJSMQswCQYDVQQIDAJTUDESMBAGA1UEBwwJU2FvIFBhdWxvMRkwFwYDVQQKDBBDaHJpcyBUZXN0aW5nIG5DMRowGAYDVQQDDBFjaHJpcy5nbHV1dHdvLm9yZzEgMB4GCSqGSIb3DQEJARYRY2hyaXNAdGVzdGluZy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaxbLrWDti7ZLAU4YVxNR6bkjt/HDfczBNF5ULlqttTbP65HgOMAl9eI8Sg+vPN2y7lk7ogQW4bJ3gcBfiBjanU8jrVMntXB8VwhZ8YYThkg1NBb9KPf9sW6FsOz+LDKNxJQeXu7jbKtb7KZvAQiFWCLil6VuKgvmjcDSnRARkSSacqVs7vM/OH9t+zRdeLA2LFEfUIW1GoOi66Tmt6hnVIhIm9I6vJOE+ym0HnyqPUQy6ZEWGbVbJ4Fn9JJmoZ3jJ1v9ZxfKJt2ZCz2HydOWJHXyg2fZwCBVdoJcydtVWQFNVJMEvQUCZNofyiJsCu+rQ033NWyhtrjlYL2fEqRnAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABDbtviA7rVkg/8wPRYPgi07jCoR9x7ZnJjMB4xHFgwIKRF7FKapUBOvqzSmYbNm3JotAdq6o9gPD3rEjQh4Sy2fptA64fquY6Fo5paVTL5AECdumv67+ziB5mtYE0iabY+QHcLHpy6kqJvFpaeUeBNypvx6SaZ3BM/9Q5VwEmmuuf+VAnY/7Q/BHVUhUBeNs9G1LOtqLTr56QyOO4ET1NKihAeE8A/R05O7fELlB2HJ4LxhMLfzwQwQIzAg5fxYrZLtjGu524SSL7Xb6BuLIitwZVAYBcXS2Up37NGHdQu9c2uHFQoxk+ZNKO1ZRUl7IE/8c6DjMTRXRpZqqRaUBco=', + entryPoint: 'https://chris.gluutwo.org/idp/profile/SAML2/POST/SSO', + issuer: 'urn:test:one', + redisCacheOptions: '{"host":"127.0.0.1", "port":6379}' + } }] } diff --git a/package-lock.json b/package-lock.json index a2f1cfe7..4c152370 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2446,6 +2446,11 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, + "denque": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -2547,7 +2552,8 @@ "double-ended-queue": { "version": "2.1.0-0", "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" + "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=", + "dev": true }, "duplexer3": { "version": "0.1.4", @@ -3396,6 +3402,34 @@ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, + "fakeredis": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fakeredis/-/fakeredis-2.0.0.tgz", + "integrity": "sha1-dS+4MoGazjG+iGaM8TWNbxQ4r8A=", + "dev": true, + "requires": { + "redis": "2.6.0-0" + }, + "dependencies": { + "redis": { + "version": "2.6.0-0", + "resolved": "https://registry.npmjs.org/redis/-/redis-2.6.0-0.tgz", + "integrity": "sha1-c/Z03T5Uo6dnA9HtJga05gC+B20=", + "dev": true, + "requires": { + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.1.0", + "redis-parser": "^1.2.0" + } + }, + "redis-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-1.3.0.tgz", + "integrity": "sha1-gG6+e7+3005NfB6e8oLvz60EEmo=", + "dev": true + } + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7794,24 +7828,33 @@ } }, "redis": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha1-ICKI4/WMSfYHnZevehDhMDrhSwI=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz", + "integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==", "requires": { - "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.2.0", - "redis-parser": "^2.6.0" + "denque": "^1.4.1", + "redis-commands": "^1.5.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" } }, "redis-commands": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", - "integrity": "sha1-gNLiBpj+aI8icSf/nlFkp90X54U=" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.6.0.tgz", + "integrity": "sha512-2jnZ0IkjZxvguITjFTrGiLyzQZcTvaw8DAaCXxZq/dsHXz7KfMQ3OUJy7Tz9vnRtZRVz6VRCPDvruvU8Ts44wQ==" + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" }, "redis-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", - "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } }, "regenerator-runtime": { "version": "0.13.5", diff --git a/package.json b/package.json index 39830b0f..c17d23b6 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "passport-windowslive": "^1.0.2", "prom-client": "^12.0.0", "ramda": "^0.26.1", - "redis": "^2.8.0", + "redis": "^3.0.2", "request": "^2.88.0", "request-promise": "^4.2.6", "sha1": "^1.1.1", @@ -77,6 +77,7 @@ "cucumber": "^6.0.5", "cz-conventional-changelog": "^3.3.0", "eslint-plugin-chai-friendly": "^0.6.0", + "fakeredis": "^2.0.0", "got": "^11.8.0", "husky": "^4.3.0", "mocha": "^8.2.1", diff --git a/server/cache-provider.js b/server/cache-provider.js index a1d058df..a193b75b 100644 --- a/server/cache-provider.js +++ b/server/cache-provider.js @@ -1,15 +1,30 @@ const redis = require('redis') const Memcached = require('memcached') const Promise = require('bluebird') -const R = require('ramda') const logger = require('./utils/logging') const OPERATION_NO_CONN = 'Attempt to operate on cache provider but connection has not been established' const promisify = (context, methodName) => Promise.promisify(context[methodName], { context: context }) +const retryStrategy = (options) => { + if (options.error && options.error.code === 'ECONNREFUSED') { + return new Error('The redis server refused the connection') + } + if (options.total_retry_time > 1000 * 60 * 60) { + // 1 min, End reconnecting after a specific timeout and flush all commands + return new Error('Redis connection retry time exhausted') + } + if (options.attempt > 10) { + // End reconnecting with built in error + return undefined + } + // reconnect after milliseconds + return Math.min(options.attempt * 100, 3000) +} + function getRedisProvider (options, exp) { logger.log2('info', 'Configuring redis cache provider for inResponseTo validation') - options = R.mergeLeft(options, { max_attempts: 3 }) + options.retry_strategy = retryStrategy let ready = false const client = redis.createClient(options) diff --git a/test/cache-provider.test.js b/test/cache-provider.test.js new file mode 100644 index 00000000..991f279a --- /dev/null +++ b/test/cache-provider.test.js @@ -0,0 +1,42 @@ +const chai = require('chai') +const rewire = require('rewire') +const cacheProviders = rewire('../server/cache-provider.js') +const testConfig = require('../config/test') +const redis = require('redis') +const fakeredis = require('fakeredis') + +const assert = chai.assert + +describe('cache provider test', () => { + const retryStrategy = cacheProviders.__get__('retryStrategy') + const testProvider = testConfig.passportConfigAuthorizedResponse.providers.find(provider => provider.id === 'saml-redis-test') + testProvider.options.retry_strategy = retryStrategy + + it('redis is not live so we should get connection error response', () => { + const client = redis.createClient(testProvider.options) + + client.on('ready', () => { + assert.fail('redis connection should not work') + }) + client.on('error', actualError => { + const expectedError = new Error('Redis connection in broken state: retry aborted.') + assert.equal(actualError.message, expectedError.message) + }) + }) + + it('redis is live so we should get connection', () => { + const client = fakeredis.createClient(testProvider.options) + + client.on('error', actualError => { + assert.fail('redis connection should work') + }) + }) + + it('getRedisProvider should return cache handlers', () => { + const getRedisProvider = cacheProviders.__get__('getRedisProvider') + const redisHandlers = getRedisProvider(testProvider.options, 100) + assert.exists(redisHandlers.save, 'Failed to initialize redis provider save handler') + assert.exists(redisHandlers.get, 'Failed to initialize redis provider get handler') + assert.exists(redisHandlers.remove, 'Failed to initialize redis provider remove handler') + }) +}) diff --git a/test/providers.test.js b/test/providers.test.js index 944e0d6f..7701f271 100644 --- a/test/providers.test.js +++ b/test/providers.test.js @@ -2,6 +2,7 @@ const chai = require('chai') const rewire = require('rewire') const providers = rewire('../server/providers.js') const testConfig = require('../config/test') +const PassportSAMLStrategy = require('passport-saml').Strategy const assert = chai.assert @@ -36,4 +37,11 @@ describe('providers setupStrategy', () => { 'Strategy is not a function!' ) }) + + it('Passport SAML Provider with redis setup should initialize the passport-saml strategy', () => { + const testProvider = testConfig.passportConfigAuthorizedResponse.providers.find(provider => provider.id === 'saml-redis-test') + + const oPassportSAMLStrategy = new PassportSAMLStrategy(testProvider.options, (profile, done) => { }) + assert.exists(oPassportSAMLStrategy, 'Failed to initialize passport saml strategy') + }) })