Skip to content

Commit

Permalink
Merge pull request #1749 from Inist-CNRS/feat/root-config
Browse files Browse the repository at this point in the history
Feat(root): Create admin config for instances
  • Loading branch information
arimet authored Oct 31, 2023
2 parents f5598b6 + 2dcb1aa commit 0d43718
Show file tree
Hide file tree
Showing 10 changed files with 284 additions and 69 deletions.
2 changes: 0 additions & 2 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
{
"username": "admin",
"password": "secret",
"userAuth": {
"username": "user",
"password": "secret"
Expand Down
20 changes: 11 additions & 9 deletions src/api/controller/api/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,30 @@ import { auth } from 'config';
import jwt from 'jsonwebtoken';
import { ADMIN_ROLE, ROOT_ROLE } from '../../../common/tools/tenantTools';

export const postLogin = date => ctx => {
export const postLogin = date => async ctx => {
if (!ctx.ezMasterConfig) {
throw new Error('Invalid EzMaster configuration.');
}

if (!ctx.ezMasterConfig.username) {
throw new Error('Invalid EzMaster configuration: missing username');
const {
username: usernameAdmin,
password: passwordAdmin,
} = await ctx.tenantCollection.findOneByName(ctx.tenant.toLowerCase());

if (!usernameAdmin) {
throw new Error('Invalid instance configuration: missing username');
}

if (!ctx.ezMasterConfig.password) {
throw new Error('Invalid EzMaster configuration: missing password.');
if (!passwordAdmin) {
throw new Error('Invalid instance configuration: missing password.');
}

const { username, password } = ctx.request.body;
const userAuth = get(ctx, 'ezMasterConfig.userAuth', {});
const rootAuth = get(ctx, 'ezMasterConfig.rootAuth', {});

let role;
if (
username === ctx.ezMasterConfig.username &&
password === ctx.ezMasterConfig.password
) {
if (username === usernameAdmin && password === passwordAdmin) {
role = ADMIN_ROLE;
}

Expand Down
93 changes: 59 additions & 34 deletions src/api/controller/api/login.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,62 @@ import { ADMIN_ROLE } from '../../../common/tools/tenantTools';
const expDate = Date.now();

describe('login', () => {
it('should set ctx.status to 401, if ctx.body.username do not match with config', () => {
it('should set ctx.status to 401, if ctx.body.username do not match with config', async () => {
const ctx = {
ezMasterConfig: {
username: 'admin',
password: 'secret',
ezMasterConfig: {},
tenantCollection: {
findOneByName: () => ({
username: 'admin',
password: 'secret',
}),
},
tenant: 'default',
request: {
body: {
username: 'not admin',
password: 'secret',
},
},
};
login(expDate)(ctx);
await login(expDate)(ctx);
expect(ctx.status).toBe(401);
});

it('should set ctx.status to 401, if ctx.body.password do not match with config', () => {
it('should set ctx.status to 401, if ctx.body.password do not match with config', async () => {
const ctx = {
ezMasterConfig: {
username: 'admin',
password: 'secret',
ezMasterConfig: {},
tenantCollection: {
findOneByName: () => ({
username: 'admin',
password: 'secret',
}),
},
tenant: 'default',
request: {
body: {
username: 'user',
password: `not secret`,
},
},
};
login(expDate)(ctx);
await login(expDate)(ctx);
expect(ctx.status).toBe(401);
});

it('should return header token and set cookie with cookie token for admin when password and user name match config', () => {
it('should return header token and set cookie with cookie token for admin when password and user name match config', async () => {
let setCall;
const ctx = {
ezMasterConfig: {
username: 'user',
password: 'secret',
ezMasterConfig: {},
tenantCollection: {
findOneByName: () => ({
username: 'admin',
password: 'secret',
}),
},
tenant: 'test',
tenant: 'default',
request: {
body: {
username: 'user',
username: 'admin',
password: 'secret',
},
},
Expand All @@ -62,11 +73,11 @@ describe('login', () => {
},
};

login(expDate)(ctx);
await login(expDate)(ctx);
expect(ctx.body).toEqual({
token: jwt.sign(
{
username: 'user',
username: 'admin',
role: ADMIN_ROLE,
exp: Math.ceil(expDate / 1000) + auth.expiresIn,
},
Expand All @@ -75,10 +86,10 @@ describe('login', () => {
role: ADMIN_ROLE,
});
expect(setCall).toEqual([
'lodex_token_test',
'lodex_token_default',
jwt.sign(
{
username: 'user',
username: 'admin',
role: ADMIN_ROLE,
exp: Math.ceil(expDate / 1000) + auth.expiresIn,
},
Expand All @@ -89,60 +100,74 @@ describe('login', () => {
});

describe('user authentication', () => {
it('should set ctx.status to 401, if ctx.body.username do not match with userAuth config', () => {
it('should set ctx.status to 401, if ctx.body.username do not match with userAuth config', async () => {
const ctx = {
ezMasterConfig: {
username: 'admin',
password: 'secret',
userAuth: {
username: 'user',
password: 'secret',
},
},
tenantCollection: {
findOneByName: () => ({
username: 'admin',
password: 'secret',
}),
},
tenant: 'default',
request: {
body: {
username: 'not user',
password: 'secret',
},
},
};
login(expDate)(ctx);
await login(expDate)(ctx);
expect(ctx.status).toBe(401);
});

it('should set ctx.status to 401, if ctx.body.password do not match with config', () => {
it('should set ctx.status to 401, if ctx.body.password do not match with config', async () => {
const ctx = {
ezMasterConfig: {
username: 'admin',
password: 'secret',
userAuth: {
username: 'user',
password: 'secret',
},
},
tenantCollection: {
findOneByName: () => ({
username: 'admin',
password: 'secret',
}),
},
tenant: 'default',
request: {
body: {
username: 'user',
password: `not secret`,
},
},
};
login(expDate)(ctx);
await login(expDate)(ctx);
expect(ctx.status).toBe(401);
});

it('should return header token and set cookie with cookie token for user when password and user name match userAuth config', () => {
it('should return header token and set cookie with cookie token for user when password and user name match userAuth config', async () => {
let setCall;
const ctx = {
ezMasterConfig: {
username: 'admin',
password: 'secret',
userAuth: {
username: 'user',
password: 'secret',
},
},
tenant: 'test',
tenantCollection: {
findOneByName: () => ({
username: 'admin',
password: 'secret',
}),
},
tenant: 'default',
request: {
body: {
username: 'user',
Expand All @@ -156,7 +181,7 @@ describe('login', () => {
},
};

login(expDate)(ctx);
await login(expDate)(ctx);
expect(ctx.body).toEqual({
token: jwt.sign(
{
Expand All @@ -169,7 +194,7 @@ describe('login', () => {
role: 'user',
});
expect(setCall).toEqual([
'lodex_token_test',
'lodex_token_default',
jwt.sign(
{
username: 'user',
Expand Down
20 changes: 18 additions & 2 deletions src/api/controller/rootAdmin.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ app.use(async (ctx, next) => {
if (!ctx.state.cookie) {
ctx.status = 401;
ctx.cookies.set('lodex_token_root', '', { expires: new Date() });
ctx.body = 'No authentication token found';
ctx.body = { message: 'No authentication token found' };
return;
}

if (ctx.state.cookie.role !== ROOT_ROLE) {
ctx.status = 401;
ctx.cookies.set('lodex_token_root', '', { expires: new Date() });
ctx.body = 'No root token found';
ctx.body = { message: 'No root token found' };
return;
}

Expand All @@ -55,6 +55,8 @@ const postTenant = async ctx => {
name,
description,
author,
username: 'admin',
password: 'secret',
createdAt: new Date(),
});
const queue = createWorkerQueue(name, 1);
Expand All @@ -63,6 +65,19 @@ const postTenant = async ctx => {
}
};

const putTenant = async (ctx, id) => {
const { description, author, username, password } = ctx.request.body;
const tenantExists = await ctx.tenantCollection.findOneById(id);

if (!tenantExists) {
ctx.throw(403, `Invalid id: "${id}"`);
}

const update = { description, author, username, password };
await ctx.tenantCollection.update(id, update);
ctx.body = await ctx.tenantCollection.findAll();
};

const deleteTenant = async ctx => {
const { _id, name } = ctx.request.body;
const tenantExists = await ctx.tenantCollection.findOne({
Expand All @@ -82,6 +97,7 @@ const deleteTenant = async ctx => {

app.use(route.get('/tenant', getTenant));
app.use(route.post('/tenant', postTenant));
app.use(route.put('/tenant/:id', putTenant));
app.use(route.delete('/tenant', deleteTenant));

app.use(async ctx => {
Expand Down
6 changes: 4 additions & 2 deletions src/api/controller/testController.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import mount from 'koa-mount';
import repositoryMiddleware, {
mongoRootAdminClient,
} from '../services/repositoryMiddleware';
import { DEFAULT_TENANT } from '../../common/tools/tenantTools';

const app = new koa();

Expand All @@ -21,8 +22,9 @@ app.use(
await ctx.db.collection('dataset').remove({});
await ctx.db.collection('subresource').remove({});
await ctx.db.collection('enrichment').remove({});
await ctx.rootAdminDb.collection('tenant').remove({});

await ctx.rootAdminDb
.collection('tenant')
.remove({ name: { $ne: DEFAULT_TENANT } });
ctx.body = { status: 'ok' };
}),
);
Expand Down
19 changes: 16 additions & 3 deletions src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,30 @@ serverAdapter.setBasePath('/bull');
const initQueueAndBullDashboard = async () => {
bullBoard.initBullBoard(serverAdapter);

const defaultQueue = createWorkerQueue(DEFAULT_TENANT, 1);
bullBoard.addDashboardQueue(DEFAULT_TENANT, defaultQueue);

// Get current tenants
const adminDb = await mongoClient('admin');
const tenantCollection = await tenant(adminDb);

const tenants = await tenantCollection.findAll();
tenants.forEach(tenant => {
const queue = createWorkerQueue(tenant.name, 1);
bullBoard.addDashboardQueue(tenant.name, queue);
});

// if tenant `default` is not in the database, we add it
if (!tenants.find(tenant => tenant.name === DEFAULT_TENANT)) {
await tenantCollection.create({
name: DEFAULT_TENANT,
description: 'Instance par défaut',
author: 'Root',
username: 'admin',
password: 'secret',
createdAt: new Date(),
});
const defaultQueue = createWorkerQueue(DEFAULT_TENANT, 1);
bullBoard.addDashboardQueue(DEFAULT_TENANT, defaultQueue);
// TODO: create default instance config.
}
};

initQueueAndBullDashboard();
Expand Down
5 changes: 0 additions & 5 deletions src/api/services/ezMasterConfig.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import expect from 'expect';

export const validateConfig = config => {
expect(config).toMatchObject({
username: /.+/,
password: /.+/,
});

if (config.userAuth) {
expect(config.userAuth).toMatchObject({
username: /.+/,
Expand Down
12 changes: 0 additions & 12 deletions src/api/services/ezMasterConfig.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,6 @@ import ezMasterConfig, { validateConfig } from './ezMasterConfig';

describe('ezMasterConfig', () => {
describe('validateConfig', () => {
it('should throw if username is not present', () => {
expect(() => validateConfig({})).toThrow();
});

it('should throw if password is not present', () => {
expect(() =>
validateConfig({
username: 'toto',
}),
).toThrow();
});

it('should throw if userAuth but no userAuth.username', () => {
expect(() =>
validateConfig({
Expand Down
Loading

0 comments on commit 0d43718

Please sign in to comment.