mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-04 00:18:40 +01:00
Implement authentication support for Unleash UI.
Closes: #261, #233, #232, #231
This commit is contained in:
parent
39d7157409
commit
f4feab89f3
3
docs/securing-unleash.md
Normal file
3
docs/securing-unleash.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Securing Unleash
|
||||||
|
|
||||||
|
TODO: write about how to secure `/api/client` and `/api/admin`
|
@ -11,6 +11,7 @@ const unleashSession = require('./middleware/session');
|
|||||||
const responseTime = require('./middleware/response-time');
|
const responseTime = require('./middleware/response-time');
|
||||||
const requestLogger = require('./middleware/request-logger');
|
const requestLogger = require('./middleware/request-logger');
|
||||||
const validator = require('./middleware/validator');
|
const validator = require('./middleware/validator');
|
||||||
|
const simpleAuthentication = require('./middleware/simple-authentication');
|
||||||
|
|
||||||
module.exports = function(config) {
|
module.exports = function(config) {
|
||||||
const app = express();
|
const app = express();
|
||||||
@ -38,6 +39,10 @@ module.exports = function(config) {
|
|||||||
app.use(baseUriPath, express.static(config.publicFolder));
|
app.use(baseUriPath, express.static(config.publicFolder));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.adminAuthentication === 'unsecure') {
|
||||||
|
simpleAuthentication(app);
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof config.preRouterHook === 'function') {
|
if (typeof config.preRouterHook === 'function') {
|
||||||
config.preRouterHook(app);
|
config.preRouterHook(app);
|
||||||
}
|
}
|
||||||
|
9
lib/authentication-required.js
Normal file
9
lib/authentication-required.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = class AuthenticationRequired {
|
||||||
|
constructor({ type, path, message }) {
|
||||||
|
this.type = type;
|
||||||
|
this.path = path;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function extractUsername(req) {
|
function extractUsername(req) {
|
||||||
return req.cookies.username || 'unknown';
|
return req.user ? req.user.email : 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = extractUsername;
|
module.exports = extractUsername;
|
||||||
|
48
lib/middleware/simple-authentication.js
Normal file
48
lib/middleware/simple-authentication.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const User = require('../user');
|
||||||
|
const AuthenticationRequired = require('../authentication-required');
|
||||||
|
|
||||||
|
function unsecureAuthentication(app) {
|
||||||
|
app.post('/api/admin/login', (req, res) => {
|
||||||
|
const user = req.body;
|
||||||
|
req.session.user = new User({ email: user.email });
|
||||||
|
res
|
||||||
|
.status(200)
|
||||||
|
.json(req.session.user)
|
||||||
|
.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use('/api/admin/', (req, res, next) => {
|
||||||
|
if (req.session.user && req.session.user.email) {
|
||||||
|
req.user = req.session.user;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use('/api/admin/', (req, res, next) => {
|
||||||
|
if (req.user) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
return res
|
||||||
|
.status('401')
|
||||||
|
.json(
|
||||||
|
new AuthenticationRequired({
|
||||||
|
path: '/api/admin/login',
|
||||||
|
type: 'unsecure',
|
||||||
|
message:
|
||||||
|
'You have to indetify yourself in order to use Unleash.',
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
// Updates active sessions every hour
|
||||||
|
req.session.nowInHours = Math.floor(Date.now() / 3600e3);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = unsecureAuthentication;
|
@ -15,6 +15,7 @@ const DEFAULT_OPTIONS = {
|
|||||||
enableRequestLogger: isDev(),
|
enableRequestLogger: isDev(),
|
||||||
secret: 'UNLEASH-SECRET',
|
secret: 'UNLEASH-SECRET',
|
||||||
sessionAge: THIRTY_DAYS,
|
sessionAge: THIRTY_DAYS,
|
||||||
|
adminAuthentication: 'unsecure',
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -7,6 +7,7 @@ const featureArchive = require('./archive.js');
|
|||||||
const events = require('./event.js');
|
const events = require('./event.js');
|
||||||
const strategies = require('./strategy');
|
const strategies = require('./strategy');
|
||||||
const metrics = require('./metrics');
|
const metrics = require('./metrics');
|
||||||
|
const user = require('./user');
|
||||||
|
|
||||||
const apiDef = {
|
const apiDef = {
|
||||||
version: 2,
|
version: 2,
|
||||||
@ -31,6 +32,7 @@ exports.router = config => {
|
|||||||
router.use('/strategies', strategies.router(config));
|
router.use('/strategies', strategies.router(config));
|
||||||
router.use('/events', events.router(config));
|
router.use('/events', events.router(config));
|
||||||
router.use('/metrics', metrics.router(config));
|
router.use('/metrics', metrics.router(config));
|
||||||
|
router.use('/user', user.router(config));
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
};
|
};
|
||||||
|
20
lib/routes/admin-api/user.js
Normal file
20
lib/routes/admin-api/user.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Router } = require('express');
|
||||||
|
|
||||||
|
exports.router = function() {
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get('/', (req, res) => {
|
||||||
|
if (req.user) {
|
||||||
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json(req.user)
|
||||||
|
.end();
|
||||||
|
} else {
|
||||||
|
return res.status(404).end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
};
|
@ -9,6 +9,8 @@ const getApp = require('./app');
|
|||||||
const { startMonitoring } = require('./metrics');
|
const { startMonitoring } = require('./metrics');
|
||||||
const { createStores } = require('./db');
|
const { createStores } = require('./db');
|
||||||
const { createOptions } = require('./options');
|
const { createOptions } = require('./options');
|
||||||
|
const User = require('./user');
|
||||||
|
const AuthenticationRequired = require('./authentication-required');
|
||||||
|
|
||||||
function createApp(options) {
|
function createApp(options) {
|
||||||
// Database dependecies (statefull)
|
// Database dependecies (statefull)
|
||||||
@ -44,4 +46,6 @@ function start(opts) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
start,
|
start,
|
||||||
|
User,
|
||||||
|
AuthenticationRequired,
|
||||||
};
|
};
|
||||||
|
14
lib/user.js
Normal file
14
lib/user.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const gravatar = require('gravatar');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
module.exports = class User {
|
||||||
|
constructor({ name, email, imageUrl } = {}) {
|
||||||
|
assert(email, 'Email is required');
|
||||||
|
this.email = email;
|
||||||
|
this.name = name;
|
||||||
|
this.imageUrl =
|
||||||
|
imageUrl || gravatar.url(email, { s: '42', d: 'retro' });
|
||||||
|
}
|
||||||
|
};
|
28
lib/user.test.js
Normal file
28
lib/user.test.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { test } = require('ava');
|
||||||
|
const User = require('./user');
|
||||||
|
|
||||||
|
test('should create user', t => {
|
||||||
|
const user = new User({ name: 'ole', email: 'some@email.com' });
|
||||||
|
t.is(user.name, 'ole');
|
||||||
|
t.is(user.email, 'some@email.com');
|
||||||
|
t.is(
|
||||||
|
user.imageUrl,
|
||||||
|
'//www.gravatar.com/avatar/d8ffeba65ee5baf57e4901690edc8e1b?s=42&d=retro'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should require email', t => {
|
||||||
|
const error = t.throws(() => {
|
||||||
|
const user = new User(); // eslint-disable-line
|
||||||
|
}, Error);
|
||||||
|
|
||||||
|
t.is(error.message, 'Email is required');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should create user with only email defined', t => {
|
||||||
|
const user = new User({ email: 'some@email.com' });
|
||||||
|
|
||||||
|
t.is(user.email, 'some@email.com');
|
||||||
|
});
|
@ -56,6 +56,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"assert": "^1.4.1",
|
||||||
"async": "^2.1.5",
|
"async": "^2.1.5",
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
"commander": "^2.9.0",
|
"commander": "^2.9.0",
|
||||||
@ -67,12 +68,15 @@
|
|||||||
"errorhandler": "^1.5.0",
|
"errorhandler": "^1.5.0",
|
||||||
"express": "^4.16.2",
|
"express": "^4.16.2",
|
||||||
"express-validator": "^4.3.0",
|
"express-validator": "^4.3.0",
|
||||||
|
"gravatar": "^1.6.0",
|
||||||
"install": "^0.10.1",
|
"install": "^0.10.1",
|
||||||
"joi": "^13.0.1",
|
"joi": "^13.0.1",
|
||||||
"knex": "^0.14.0",
|
"knex": "^0.14.0",
|
||||||
"log4js": "^2.0.0",
|
"log4js": "^2.0.0",
|
||||||
"moment": "^2.19.3",
|
"moment": "^2.19.3",
|
||||||
"parse-database-url": "^0.3.0",
|
"parse-database-url": "^0.3.0",
|
||||||
|
"passport": "^0.4.0",
|
||||||
|
"passport-google-auth": "^1.0.2",
|
||||||
"pg": "^7.4.0",
|
"pg": "^7.4.0",
|
||||||
"pkginfo": "^0.4.1",
|
"pkginfo": "^0.4.1",
|
||||||
"prom-client": "^10.0.4",
|
"prom-client": "^10.0.4",
|
||||||
|
36
test/e2e/api/admin/feature.auth.e2e.test.js
Normal file
36
test/e2e/api/admin/feature.auth.e2e.test.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { test } = require('ava');
|
||||||
|
const { setupAppWithAuth } = require('./../../helpers/test-helper');
|
||||||
|
|
||||||
|
test.serial('creates new feature toggle with createdBy', async t => {
|
||||||
|
t.plan(1);
|
||||||
|
const { request, destroy } = await setupAppWithAuth('feature_api_auth');
|
||||||
|
// Login
|
||||||
|
await request.post('/api/admin/login').send({
|
||||||
|
email: 'user@mail.com',
|
||||||
|
});
|
||||||
|
|
||||||
|
// create toggle
|
||||||
|
await request.post('/api/admin/features').send({
|
||||||
|
name: 'com.test.Username',
|
||||||
|
enabled: false,
|
||||||
|
strategies: [{ name: 'default' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
await request
|
||||||
|
.get('/api/admin/events')
|
||||||
|
.expect(res => {
|
||||||
|
t.true(res.body.events[0].createdBy === 'user@mail.com');
|
||||||
|
})
|
||||||
|
.then(destroy);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial('should require authenticated user', async t => {
|
||||||
|
t.plan(0);
|
||||||
|
const { request, destroy } = await setupAppWithAuth('feature_api_auth');
|
||||||
|
return request
|
||||||
|
.get('/api/admin/features')
|
||||||
|
.expect(401)
|
||||||
|
.then(destroy);
|
||||||
|
});
|
62
test/e2e/api/admin/feature.custom-auth.e2e.test.js
Normal file
62
test/e2e/api/admin/feature.custom-auth.e2e.test.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { test } = require('ava');
|
||||||
|
const { setupAppWithCustomAuth } = require('./../../helpers/test-helper');
|
||||||
|
const AuthenticationRequired = require('./../../../../lib/authentication-required');
|
||||||
|
const User = require('./../../../../lib/user');
|
||||||
|
|
||||||
|
test.serial('should require authenticated user', async t => {
|
||||||
|
t.plan(0);
|
||||||
|
const preHook = app => {
|
||||||
|
app.use('/api/admin/', (req, res) =>
|
||||||
|
res
|
||||||
|
.status('401')
|
||||||
|
.json(
|
||||||
|
new AuthenticationRequired({
|
||||||
|
path: '/api/admin/login',
|
||||||
|
type: 'custom',
|
||||||
|
message: `You have to identify yourself.`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.end()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const { request, destroy } = await setupAppWithCustomAuth(
|
||||||
|
'feature_api_custom_auth',
|
||||||
|
preHook
|
||||||
|
);
|
||||||
|
return request
|
||||||
|
.get('/api/admin/features')
|
||||||
|
.expect(401)
|
||||||
|
.then(destroy);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial('creates new feature toggle with createdBy', async t => {
|
||||||
|
t.plan(1);
|
||||||
|
const user = new User({ email: 'custom-user@mail.com' });
|
||||||
|
|
||||||
|
const preHook = app => {
|
||||||
|
app.use('/api/admin/', (req, res, next) => {
|
||||||
|
req.user = user;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const { request, destroy } = await setupAppWithCustomAuth(
|
||||||
|
'feature_api_custom_auth',
|
||||||
|
preHook
|
||||||
|
);
|
||||||
|
|
||||||
|
// create toggle
|
||||||
|
await request.post('/api/admin/features').send({
|
||||||
|
name: 'com.test.Username',
|
||||||
|
enabled: false,
|
||||||
|
strategies: [{ name: 'default' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
await request
|
||||||
|
.get('/api/admin/events')
|
||||||
|
.expect(res => {
|
||||||
|
t.true(res.body.events[0].createdBy === user.email);
|
||||||
|
})
|
||||||
|
.then(destroy);
|
||||||
|
});
|
@ -50,22 +50,18 @@ test.serial('creates new feature toggle', async t => {
|
|||||||
.then(destroy);
|
.then(destroy);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.serial('creates new feature toggle with createdBy', async t => {
|
test.serial('creates new feature toggle with createdBy unknown', async t => {
|
||||||
t.plan(1);
|
t.plan(1);
|
||||||
const { request, destroy } = await setupApp('feature_api_serial');
|
const { request, destroy } = await setupApp('feature_api_serial');
|
||||||
await request
|
await request.post('/api/admin/features').send({
|
||||||
.post('/api/admin/features')
|
name: 'com.test.Username',
|
||||||
.send({
|
enabled: false,
|
||||||
name: 'com.test.Username',
|
strategies: [{ name: 'default' }],
|
||||||
enabled: false,
|
});
|
||||||
strategies: [{ name: 'default' }],
|
|
||||||
})
|
|
||||||
.set('Cookie', ['username=ivaosthu'])
|
|
||||||
.set('Content-Type', 'application/json');
|
|
||||||
await request
|
await request
|
||||||
.get('/api/admin/events')
|
.get('/api/admin/events')
|
||||||
.expect(res => {
|
.expect(res => {
|
||||||
t.true(res.body.events[0].createdBy === 'ivaosthu');
|
t.true(res.body.events[0].createdBy === 'unknown');
|
||||||
})
|
})
|
||||||
.then(destroy);
|
.then(destroy);
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,36 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { test } = require('ava');
|
const { test } = require('ava');
|
||||||
|
const { setupApp } = require('./../../helpers/test-helper');
|
||||||
|
|
||||||
test.todo('e2e client feature');
|
test.serial('returns three feature toggles', async t => {
|
||||||
|
const { request, destroy } = await setupApp('feature_api_client');
|
||||||
|
return request
|
||||||
|
.get('/api/client/features')
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
.expect(res => {
|
||||||
|
t.true(res.body.features.length === 3);
|
||||||
|
})
|
||||||
|
.then(destroy);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial('gets a feature by name', async t => {
|
||||||
|
t.plan(0);
|
||||||
|
const { request, destroy } = await setupApp('feature_api_client');
|
||||||
|
return request
|
||||||
|
.get('/api/client/features/featureX')
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
.then(destroy);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial('cant get feature that dose not exist', async t => {
|
||||||
|
t.plan(0);
|
||||||
|
const { request, destroy } = await setupApp('feature_api_client');
|
||||||
|
return request
|
||||||
|
.get('/api/client/features/myfeature')
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(404)
|
||||||
|
.then(destroy);
|
||||||
|
});
|
||||||
|
70
test/e2e/helpers/database-init.js
Normal file
70
test/e2e/helpers/database-init.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const migrator = require('../../../migrator');
|
||||||
|
const { createStores } = require('../../../lib/db');
|
||||||
|
const { createDb } = require('../../../lib/db/db-pool');
|
||||||
|
|
||||||
|
const dbState = require('./database.json');
|
||||||
|
|
||||||
|
require('db-migrate-shared').log.silence(true);
|
||||||
|
|
||||||
|
// because of migrator bug
|
||||||
|
delete process.env.DATABASE_URL;
|
||||||
|
|
||||||
|
// because of db-migrate bug (https://github.com/Unleash/unleash/issues/171)
|
||||||
|
process.setMaxListeners(0);
|
||||||
|
|
||||||
|
async function resetDatabase(stores) {
|
||||||
|
return Promise.all([
|
||||||
|
stores.db('strategies').del(),
|
||||||
|
stores.db('features').del(),
|
||||||
|
stores.db('client_applications').del(),
|
||||||
|
stores.db('client_instances').del(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupDatabase(stores) {
|
||||||
|
const updates = [];
|
||||||
|
updates.push(...createStrategies(stores.strategyStore));
|
||||||
|
updates.push(...createFeatures(stores.featureToggleStore));
|
||||||
|
updates.push(...createClientInstance(stores.clientInstanceStore));
|
||||||
|
updates.push(...createApplications(stores.clientApplicationsStore));
|
||||||
|
|
||||||
|
await Promise.all(updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createStrategies(store) {
|
||||||
|
return dbState.strategies.map(s => store._createStrategy(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createApplications(store) {
|
||||||
|
return dbState.applications.map(a => store.upsert(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createClientInstance(store) {
|
||||||
|
return dbState.clientInstances.map(i => store.insert(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFeatures(store) {
|
||||||
|
return dbState.features.map(f => store._createFeature(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function init(databaseSchema = 'test') {
|
||||||
|
const options = {
|
||||||
|
databaseUrl: require('./database-config').getDatabaseUrl(),
|
||||||
|
databaseSchema,
|
||||||
|
minPool: 0,
|
||||||
|
maxPool: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const db = createDb(options);
|
||||||
|
|
||||||
|
await db.raw(`CREATE SCHEMA IF NOT EXISTS ${options.databaseSchema}`);
|
||||||
|
await migrator(options);
|
||||||
|
await db.destroy();
|
||||||
|
const stores = await createStores(options);
|
||||||
|
await resetDatabase(stores);
|
||||||
|
await setupDatabase(stores);
|
||||||
|
|
||||||
|
return stores;
|
||||||
|
};
|
113
test/e2e/helpers/database.json
Normal file
113
test/e2e/helpers/database.json
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
{
|
||||||
|
"strategies": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"description": "Default on or off Strategy.",
|
||||||
|
"parameters": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "usersWithEmail",
|
||||||
|
"description": "Active for users defined in the comma-separated emails-parameter.",
|
||||||
|
"parameters": [{
|
||||||
|
"name": "emails",
|
||||||
|
"type": "string"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"applications": [
|
||||||
|
{
|
||||||
|
"appName": "demo-app-1",
|
||||||
|
"strategies": ["default"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appName": "demo-app-2",
|
||||||
|
"strategies": ["default",
|
||||||
|
"extra"
|
||||||
|
],
|
||||||
|
"description": "hello"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"clientInstances": [
|
||||||
|
{
|
||||||
|
"appName": "demo-app-1",
|
||||||
|
"instanceId": "test-1",
|
||||||
|
"strategies": ["default"],
|
||||||
|
"started": 1516026938494,
|
||||||
|
"interval": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appName": "demo-seed-2",
|
||||||
|
"instanceId": "test-2",
|
||||||
|
"strategies": ["default"],
|
||||||
|
"started": 1516026938494,
|
||||||
|
"interval": 10
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"name": "featureX",
|
||||||
|
"description": "the #1 feature",
|
||||||
|
"enabled": true,
|
||||||
|
"strategies": [{
|
||||||
|
"name": "default",
|
||||||
|
"parameters": {}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "featureY",
|
||||||
|
"description": "soon to be the #1 feature",
|
||||||
|
"enabled": false,
|
||||||
|
"strategies": [{
|
||||||
|
"name": "baz",
|
||||||
|
"parameters": {
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "featureZ",
|
||||||
|
"description": "terrible feature",
|
||||||
|
"enabled": true,
|
||||||
|
"strategies": [{
|
||||||
|
"name": "baz",
|
||||||
|
"parameters": {
|
||||||
|
"foo": "rab"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "featureArchivedX",
|
||||||
|
"description": "the #1 feature",
|
||||||
|
"enabled": true,
|
||||||
|
"archived": true,
|
||||||
|
"strategies": [{
|
||||||
|
"name": "default",
|
||||||
|
"parameters": {}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "featureArchivedY",
|
||||||
|
"description": "soon to be the #1 feature",
|
||||||
|
"enabled": false,
|
||||||
|
"archived": true,
|
||||||
|
"strategies": [{
|
||||||
|
"name": "baz",
|
||||||
|
"parameters": {
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "featureArchivedZ",
|
||||||
|
"description": "terrible feature",
|
||||||
|
"enabled": true,
|
||||||
|
"archived": true,
|
||||||
|
"strategies": [{
|
||||||
|
"name": "baz",
|
||||||
|
"parameters": {
|
||||||
|
"foo": "rab"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -2,198 +2,52 @@
|
|||||||
|
|
||||||
process.env.NODE_ENV = 'test';
|
process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
// because of db-migrate bug (https://github.com/Unleash/unleash/issues/171)
|
|
||||||
process.setMaxListeners(0);
|
|
||||||
|
|
||||||
const supertest = require('supertest');
|
const supertest = require('supertest');
|
||||||
const migrator = require('../../../migrator');
|
|
||||||
const { createStores } = require('../../../lib/db');
|
|
||||||
const { createDb } = require('../../../lib/db/db-pool');
|
|
||||||
const getApp = require('../../../lib/app');
|
|
||||||
require('db-migrate-shared').log.silence(true);
|
|
||||||
|
|
||||||
// because of migrator bug
|
const getApp = require('../../../lib/app');
|
||||||
delete process.env.DATABASE_URL;
|
const dbInit = require('./database-init');
|
||||||
|
|
||||||
const { EventEmitter } = require('events');
|
const { EventEmitter } = require('events');
|
||||||
const eventBus = new EventEmitter();
|
const eventBus = new EventEmitter();
|
||||||
|
|
||||||
function createApp(databaseSchema = 'test') {
|
function createApp(stores, adminAuthentication = 'none', preHook) {
|
||||||
const options = {
|
return getApp({
|
||||||
databaseUrl: require('./database-config').getDatabaseUrl(),
|
stores,
|
||||||
databaseSchema,
|
eventBus,
|
||||||
minPool: 0,
|
preHook,
|
||||||
maxPool: 0,
|
adminAuthentication,
|
||||||
};
|
secret: 'super-secret',
|
||||||
const db = createDb({
|
sessionAge: 4000,
|
||||||
databaseUrl: options.databaseUrl,
|
|
||||||
minPool: 0,
|
|
||||||
maxPool: 0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return db
|
|
||||||
.raw(`CREATE SCHEMA IF NOT EXISTS ${options.databaseSchema}`)
|
|
||||||
.then(() => migrator(options))
|
|
||||||
.then(() => {
|
|
||||||
db.destroy();
|
|
||||||
const stores = createStores(options);
|
|
||||||
const app = getApp({ stores, eventBus });
|
|
||||||
return {
|
|
||||||
stores,
|
|
||||||
request: supertest(app),
|
|
||||||
destroy() {
|
|
||||||
return stores.db.destroy();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createStrategies(stores) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: 'default',
|
|
||||||
description: 'Default on or off Strategy.',
|
|
||||||
parameters: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'usersWithEmail',
|
|
||||||
description:
|
|
||||||
'Active for users defined in the comma-separated emails-parameter.',
|
|
||||||
parameters: [{ name: 'emails', type: 'string' }],
|
|
||||||
},
|
|
||||||
].map(strategy => stores.strategyStore._createStrategy(strategy));
|
|
||||||
}
|
|
||||||
|
|
||||||
function createApplications(stores) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
appName: 'demo-app-1',
|
|
||||||
strategies: ['default'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
appName: 'demo-app-2',
|
|
||||||
strategies: ['default', 'extra'],
|
|
||||||
description: 'hello',
|
|
||||||
},
|
|
||||||
].map(client => stores.clientApplicationsStore.upsert(client));
|
|
||||||
}
|
|
||||||
|
|
||||||
function createClientInstance(stores) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
appName: 'demo-app-1',
|
|
||||||
instanceId: 'test-1',
|
|
||||||
strategies: ['default'],
|
|
||||||
started: Date.now(),
|
|
||||||
interval: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
appName: 'demo-seed-2',
|
|
||||||
instanceId: 'test-2',
|
|
||||||
strategies: ['default'],
|
|
||||||
started: Date.now(),
|
|
||||||
interval: 10,
|
|
||||||
},
|
|
||||||
].map(client => stores.clientInstanceStore.insert(client));
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFeatures(stores) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: 'featureX',
|
|
||||||
description: 'the #1 feature',
|
|
||||||
enabled: true,
|
|
||||||
strategies: [{ name: 'default', parameters: {} }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'featureY',
|
|
||||||
description: 'soon to be the #1 feature',
|
|
||||||
enabled: false,
|
|
||||||
strategies: [
|
|
||||||
{
|
|
||||||
name: 'baz',
|
|
||||||
parameters: {
|
|
||||||
foo: 'bar',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'featureZ',
|
|
||||||
description: 'terrible feature',
|
|
||||||
enabled: true,
|
|
||||||
strategies: [
|
|
||||||
{
|
|
||||||
name: 'baz',
|
|
||||||
parameters: {
|
|
||||||
foo: 'rab',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'featureArchivedX',
|
|
||||||
description: 'the #1 feature',
|
|
||||||
enabled: true,
|
|
||||||
archived: true,
|
|
||||||
strategies: [{ name: 'default', parameters: {} }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'featureArchivedY',
|
|
||||||
description: 'soon to be the #1 feature',
|
|
||||||
enabled: false,
|
|
||||||
archived: true,
|
|
||||||
strategies: [
|
|
||||||
{
|
|
||||||
name: 'baz',
|
|
||||||
parameters: {
|
|
||||||
foo: 'bar',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'featureArchivedZ',
|
|
||||||
description: 'terrible feature',
|
|
||||||
enabled: true,
|
|
||||||
archived: true,
|
|
||||||
strategies: [
|
|
||||||
{
|
|
||||||
name: 'baz',
|
|
||||||
parameters: {
|
|
||||||
foo: 'rab',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
].map(feature => stores.featureToggleStore._createFeature(feature));
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetDatabase(stores) {
|
|
||||||
return Promise.all([
|
|
||||||
stores.db('strategies').del(),
|
|
||||||
stores.db('features').del(),
|
|
||||||
stores.db('client_applications').del(),
|
|
||||||
stores.db('client_instances').del(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupDatabase(stores) {
|
|
||||||
return Promise.all(
|
|
||||||
createStrategies(stores).concat(
|
|
||||||
createFeatures(stores)
|
|
||||||
.concat(createClientInstance(stores))
|
|
||||||
.concat(createApplications(stores))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
setupApp(name) {
|
async setupApp(name) {
|
||||||
return createApp(name).then(app =>
|
const stores = await dbInit(name);
|
||||||
resetDatabase(app.stores)
|
const app = createApp(stores);
|
||||||
.then(() => setupDatabase(app.stores))
|
|
||||||
.then(() => app)
|
return {
|
||||||
);
|
request: supertest.agent(app),
|
||||||
|
destroy: () => stores.db.destroy(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async setupAppWithAuth(name) {
|
||||||
|
const stores = await dbInit(name);
|
||||||
|
const app = createApp(stores, 'unsecure');
|
||||||
|
|
||||||
|
return {
|
||||||
|
request: supertest.agent(app),
|
||||||
|
destroy: () => stores.db.destroy(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async setupAppWithCustomAuth(name, preHook) {
|
||||||
|
const stores = await dbInit(name);
|
||||||
|
const app = createApp(stores, 'custom', preHook);
|
||||||
|
|
||||||
|
return {
|
||||||
|
request: supertest.agent(app),
|
||||||
|
destroy: () => stores.db.destroy(),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
177
yarn.lock
177
yarn.lock
@ -298,6 +298,12 @@ assert-plus@^0.2.0:
|
|||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
|
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
|
||||||
|
|
||||||
|
assert@^1.4.1:
|
||||||
|
version "1.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
|
||||||
|
dependencies:
|
||||||
|
util "0.10.3"
|
||||||
|
|
||||||
ast-types@0.x.x:
|
ast-types@0.x.x:
|
||||||
version "0.9.14"
|
version "0.9.14"
|
||||||
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.14.tgz#d34ba5dffb9d15a44351fd2a9d82e4ab2838b5ba"
|
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.14.tgz#d34ba5dffb9d15a44351fd2a9d82e4ab2838b5ba"
|
||||||
@ -324,7 +330,7 @@ async@~1.0.0:
|
|||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9"
|
resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9"
|
||||||
|
|
||||||
async@~2.1.2:
|
async@~2.1.2, async@~2.1.4:
|
||||||
version "2.1.5"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc"
|
resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -756,6 +762,10 @@ balanced-match@^1.0.0:
|
|||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
|
|
||||||
|
base64url@2.0.0, base64url@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64url/-/base64url-2.0.0.tgz#eac16e03ea1438eff9423d69baa36262ed1f70bb"
|
||||||
|
|
||||||
bcrypt-pbkdf@^1.0.0:
|
bcrypt-pbkdf@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
|
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
|
||||||
@ -786,6 +796,10 @@ bluebird@^3.0.0, bluebird@^3.1.1, bluebird@^3.4.6:
|
|||||||
version "3.5.1"
|
version "3.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
|
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
|
||||||
|
|
||||||
|
blueimp-md5@^2.3.0:
|
||||||
|
version "2.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.10.0.tgz#02f0843921f90dca14f5b8920a38593201d6964d"
|
||||||
|
|
||||||
body-parser@1.18.2, body-parser@^1.18.2:
|
body-parser@1.18.2, body-parser@^1.18.2:
|
||||||
version "1.18.2"
|
version "1.18.2"
|
||||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454"
|
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454"
|
||||||
@ -850,6 +864,10 @@ buf-compare@^1.0.0:
|
|||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/buf-compare/-/buf-compare-1.0.1.tgz#fef28da8b8113a0a0db4430b0b6467b69730b34a"
|
resolved "https://registry.yarnpkg.com/buf-compare/-/buf-compare-1.0.1.tgz#fef28da8b8113a0a0db4430b0b6467b69730b34a"
|
||||||
|
|
||||||
|
buffer-equal-constant-time@1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||||
|
|
||||||
buffer-writer@1.0.1:
|
buffer-writer@1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08"
|
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08"
|
||||||
@ -920,6 +938,10 @@ camelcase@^2.0.0:
|
|||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
|
||||||
|
|
||||||
|
camelcase@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
|
||||||
|
|
||||||
camelcase@^4.0.0, camelcase@^4.1.0:
|
camelcase@^4.0.0, camelcase@^4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
|
||||||
@ -1552,6 +1574,13 @@ ecc-jsbn@~0.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
jsbn "~0.1.0"
|
jsbn "~0.1.0"
|
||||||
|
|
||||||
|
ecdsa-sig-formatter@1.0.9:
|
||||||
|
version "1.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz#4bc926274ec3b5abb5016e7e1d60921ac262b2a1"
|
||||||
|
dependencies:
|
||||||
|
base64url "^2.0.0"
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
ee-first@1.1.1:
|
ee-first@1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||||
@ -1564,6 +1593,10 @@ element-class@^0.2.0:
|
|||||||
version "0.2.2"
|
version "0.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/element-class/-/element-class-0.2.2.tgz#9d3bbd0767f9013ef8e1c8ebe722c1402a60050e"
|
resolved "https://registry.yarnpkg.com/element-class/-/element-class-0.2.2.tgz#9d3bbd0767f9013ef8e1c8ebe722c1402a60050e"
|
||||||
|
|
||||||
|
email-validator@^1.0.7:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-1.1.1.tgz#b07f3be7bac1dc099bc43e75f6ae399f552d5a80"
|
||||||
|
|
||||||
empower-core@^0.6.1:
|
empower-core@^0.6.1:
|
||||||
version "0.6.2"
|
version "0.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/empower-core/-/empower-core-0.6.2.tgz#5adef566088e31fba80ba0a36df47d7094169144"
|
resolved "https://registry.yarnpkg.com/empower-core/-/empower-core-0.6.2.tgz#5adef566088e31fba80ba0a36df47d7094169144"
|
||||||
@ -2307,6 +2340,29 @@ globby@^6.0.0:
|
|||||||
pify "^2.0.0"
|
pify "^2.0.0"
|
||||||
pinkie-promise "^2.0.0"
|
pinkie-promise "^2.0.0"
|
||||||
|
|
||||||
|
google-auth-library@~0.10.0:
|
||||||
|
version "0.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-0.10.0.tgz#6e15babee85fd1dd14d8d128a295b6838d52136e"
|
||||||
|
dependencies:
|
||||||
|
gtoken "^1.2.1"
|
||||||
|
jws "^3.1.4"
|
||||||
|
lodash.noop "^3.0.1"
|
||||||
|
request "^2.74.0"
|
||||||
|
|
||||||
|
google-p12-pem@^0.1.0:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-0.1.2.tgz#33c46ab021aa734fa0332b3960a9a3ffcb2f3177"
|
||||||
|
dependencies:
|
||||||
|
node-forge "^0.7.1"
|
||||||
|
|
||||||
|
googleapis@^16.0.0:
|
||||||
|
version "16.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-16.1.0.tgz#0f19f2d70572d918881a0f626e3b1a2fa8629576"
|
||||||
|
dependencies:
|
||||||
|
async "~2.1.4"
|
||||||
|
google-auth-library "~0.10.0"
|
||||||
|
string-template "~1.0.0"
|
||||||
|
|
||||||
got@^6.7.1:
|
got@^6.7.1:
|
||||||
version "6.7.1"
|
version "6.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
|
resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
|
||||||
@ -2327,6 +2383,24 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2:
|
|||||||
version "4.1.11"
|
version "4.1.11"
|
||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
|
||||||
|
|
||||||
|
gravatar@^1.6.0:
|
||||||
|
version "1.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/gravatar/-/gravatar-1.6.0.tgz#8bdc9b786ca725a8e7076416d1731f8d3331c976"
|
||||||
|
dependencies:
|
||||||
|
blueimp-md5 "^2.3.0"
|
||||||
|
email-validator "^1.0.7"
|
||||||
|
querystring "0.2.0"
|
||||||
|
yargs "^6.0.0"
|
||||||
|
|
||||||
|
gtoken@^1.2.1:
|
||||||
|
version "1.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-1.2.3.tgz#5509571b8afd4322e124cf66cf68115284c476d8"
|
||||||
|
dependencies:
|
||||||
|
google-p12-pem "^0.1.0"
|
||||||
|
jws "^3.0.0"
|
||||||
|
mime "^1.4.1"
|
||||||
|
request "^2.72.0"
|
||||||
|
|
||||||
handlebars@^4.0.3:
|
handlebars@^4.0.3:
|
||||||
version "4.0.11"
|
version "4.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"
|
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"
|
||||||
@ -2616,6 +2690,10 @@ inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, i
|
|||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||||
|
|
||||||
|
inherits@2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
||||||
|
|
||||||
ini@^1.3.4, ini@~1.3.0:
|
ini@^1.3.4, ini@~1.3.0:
|
||||||
version "1.3.4"
|
version "1.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
|
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
|
||||||
@ -3097,6 +3175,23 @@ jsprim@^1.2.2:
|
|||||||
json-schema "0.2.3"
|
json-schema "0.2.3"
|
||||||
verror "1.10.0"
|
verror "1.10.0"
|
||||||
|
|
||||||
|
jwa@^1.1.4:
|
||||||
|
version "1.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.5.tgz#a0552ce0220742cd52e153774a32905c30e756e5"
|
||||||
|
dependencies:
|
||||||
|
base64url "2.0.0"
|
||||||
|
buffer-equal-constant-time "1.0.1"
|
||||||
|
ecdsa-sig-formatter "1.0.9"
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
|
jws@^3.0.0, jws@^3.1.4:
|
||||||
|
version "3.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.4.tgz#f9e8b9338e8a847277d6444b1464f61880e050a2"
|
||||||
|
dependencies:
|
||||||
|
base64url "^2.0.0"
|
||||||
|
jwa "^1.1.4"
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
keygrip@~1.0.2:
|
keygrip@~1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91"
|
resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91"
|
||||||
@ -3358,6 +3453,10 @@ lodash.merge@^4.6.0:
|
|||||||
version "4.6.0"
|
version "4.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
|
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
|
||||||
|
|
||||||
|
lodash.noop@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c"
|
||||||
|
|
||||||
lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.0:
|
lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.0:
|
||||||
version "4.17.4"
|
version "4.17.4"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
|
||||||
@ -3670,6 +3769,10 @@ node-fetch@^1.0.1:
|
|||||||
encoding "^0.1.11"
|
encoding "^0.1.11"
|
||||||
is-stream "^1.0.1"
|
is-stream "^1.0.1"
|
||||||
|
|
||||||
|
node-forge@^0.7.1:
|
||||||
|
version "0.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300"
|
||||||
|
|
||||||
node-fs@~0.1.5:
|
node-fs@~0.1.5:
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/node-fs/-/node-fs-0.1.7.tgz#32323cccb46c9fbf0fc11812d45021cc31d325bb"
|
resolved "https://registry.yarnpkg.com/node-fs/-/node-fs-0.1.7.tgz#32323cccb46c9fbf0fc11812d45021cc31d325bb"
|
||||||
@ -3940,6 +4043,12 @@ os-homedir@^1.0.0, os-homedir@^1.0.1:
|
|||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
|
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
|
||||||
|
|
||||||
|
os-locale@^1.4.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
|
||||||
|
dependencies:
|
||||||
|
lcid "^1.0.0"
|
||||||
|
|
||||||
os-locale@^2.0.0:
|
os-locale@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
|
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
|
||||||
@ -4084,6 +4193,24 @@ parseurl@~1.3.2:
|
|||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
|
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
|
||||||
|
|
||||||
|
passport-google-auth@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/passport-google-auth/-/passport-google-auth-1.0.2.tgz#8b300b5aa442ef433de1d832ed3112877d0b2938"
|
||||||
|
dependencies:
|
||||||
|
googleapis "^16.0.0"
|
||||||
|
passport-strategy "1.x"
|
||||||
|
|
||||||
|
passport-strategy@1.x, passport-strategy@1.x.x:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
|
||||||
|
|
||||||
|
passport@^0.4.0:
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/passport/-/passport-0.4.0.tgz#c5095691347bd5ad3b5e180238c3914d16f05811"
|
||||||
|
dependencies:
|
||||||
|
passport-strategy "1.x.x"
|
||||||
|
pause "0.0.1"
|
||||||
|
|
||||||
path-exists@^2.0.0:
|
path-exists@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
|
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
|
||||||
@ -4144,6 +4271,10 @@ path-type@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
pify "^2.0.0"
|
pify "^2.0.0"
|
||||||
|
|
||||||
|
pause@0.0.1:
|
||||||
|
version "0.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d"
|
||||||
|
|
||||||
performance-now@^0.2.0:
|
performance-now@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
|
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
|
||||||
@ -4444,6 +4575,10 @@ query-string@^4.2.2:
|
|||||||
object-assign "^4.1.0"
|
object-assign "^4.1.0"
|
||||||
strict-uri-encode "^1.0.0"
|
strict-uri-encode "^1.0.0"
|
||||||
|
|
||||||
|
querystring@0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||||
|
|
||||||
randomatic@^1.1.3:
|
randomatic@^1.1.3:
|
||||||
version "1.1.7"
|
version "1.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c"
|
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c"
|
||||||
@ -4805,7 +4940,7 @@ request@2.81.0:
|
|||||||
tunnel-agent "^0.6.0"
|
tunnel-agent "^0.6.0"
|
||||||
uuid "^3.0.0"
|
uuid "^3.0.0"
|
||||||
|
|
||||||
request@^2.0.0, request@^2.74.0, request@^2.79.0:
|
request@^2.0.0, request@^2.72.0, request@^2.74.0, request@^2.79.0:
|
||||||
version "2.83.0"
|
version "2.83.0"
|
||||||
resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
|
resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -5275,6 +5410,10 @@ strict-uri-encode@^1.0.0:
|
|||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||||
|
|
||||||
|
string-template@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96"
|
||||||
|
|
||||||
string-width@^1.0.1, string-width@^1.0.2:
|
string-width@^1.0.1, string-width@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
|
||||||
@ -5699,6 +5838,12 @@ util-deprecate@~1.0.1:
|
|||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
|
||||||
|
util@0.10.3:
|
||||||
|
version "0.10.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
|
||||||
|
dependencies:
|
||||||
|
inherits "2.0.1"
|
||||||
|
|
||||||
utile@0.3.x:
|
utile@0.3.x:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/utile/-/utile-0.3.0.tgz#1352c340eb820e4d8ddba039a4fbfaa32ed4ef3a"
|
resolved "https://registry.yarnpkg.com/utile/-/utile-0.3.0.tgz#1352c340eb820e4d8ddba039a4fbfaa32ed4ef3a"
|
||||||
@ -5769,6 +5914,10 @@ when@~2.0.1:
|
|||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/when/-/when-2.0.1.tgz#8d872fe15e68424c91b4b724e848e0807dab6642"
|
resolved "https://registry.yarnpkg.com/when/-/when-2.0.1.tgz#8d872fe15e68424c91b4b724e848e0807dab6642"
|
||||||
|
|
||||||
|
which-module@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
|
||||||
|
|
||||||
which-module@^2.0.0:
|
which-module@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||||
@ -5894,6 +6043,12 @@ yallist@^3.0.2:
|
|||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9"
|
||||||
|
|
||||||
|
yargs-parser@^4.2.0:
|
||||||
|
version "4.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c"
|
||||||
|
dependencies:
|
||||||
|
camelcase "^3.0.0"
|
||||||
|
|
||||||
yargs-parser@^8.0.0:
|
yargs-parser@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.0.0.tgz#21d476330e5a82279a4b881345bf066102e219c6"
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.0.0.tgz#21d476330e5a82279a4b881345bf066102e219c6"
|
||||||
@ -5917,6 +6072,24 @@ yargs@^10.0.3:
|
|||||||
y18n "^3.2.1"
|
y18n "^3.2.1"
|
||||||
yargs-parser "^8.0.0"
|
yargs-parser "^8.0.0"
|
||||||
|
|
||||||
|
yargs@^6.0.0:
|
||||||
|
version "6.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
|
||||||
|
dependencies:
|
||||||
|
camelcase "^3.0.0"
|
||||||
|
cliui "^3.2.0"
|
||||||
|
decamelize "^1.1.1"
|
||||||
|
get-caller-file "^1.0.1"
|
||||||
|
os-locale "^1.4.0"
|
||||||
|
read-pkg-up "^1.0.1"
|
||||||
|
require-directory "^2.1.1"
|
||||||
|
require-main-filename "^1.0.1"
|
||||||
|
set-blocking "^2.0.0"
|
||||||
|
string-width "^1.0.2"
|
||||||
|
which-module "^1.0.0"
|
||||||
|
y18n "^3.2.1"
|
||||||
|
yargs-parser "^4.2.0"
|
||||||
|
|
||||||
yargs@~3.10.0:
|
yargs@~3.10.0:
|
||||||
version "3.10.0"
|
version "3.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
|
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
|
||||||
|
Loading…
Reference in New Issue
Block a user