feat: add POST /api/v1/installation/users to seed user
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
import User from '../../../../../models/user.js';
|
||||
import Config from '../../../../../models/config.js';
|
||||
|
||||
export default async (request, response) => {
|
||||
const { email, password, fullName } = request.body;
|
||||
|
||||
await User.createAdminUser({ email, password, fullName });
|
||||
|
||||
await Config.markInstallationCompleted();
|
||||
|
||||
response.status(204).end();
|
||||
};
|
@@ -0,0 +1,62 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import app from '../../../../../app.js';
|
||||
import Config from '../../../../../models/config.js';
|
||||
import User from '../../../../../models/user.js';
|
||||
import { createRole } from '../../../../../../test/factories/role';
|
||||
import { createInstallationCompletedConfig } from '../../../../../../test/factories/config';
|
||||
|
||||
describe('POST /api/v1/installation/users', () => {
|
||||
let adminRole;
|
||||
|
||||
beforeEach(async () => {
|
||||
adminRole = await createRole({
|
||||
name: 'Admin',
|
||||
key: 'admin',
|
||||
})
|
||||
});
|
||||
|
||||
describe('for incomplete installations', () => {
|
||||
it('should respond with HTTP 204 with correct payload', async () => {
|
||||
expect(await Config.isInstallationCompleted()).toBe(false);
|
||||
|
||||
await request(app)
|
||||
.post('/api/v1/installation/users')
|
||||
.send({
|
||||
email: 'user@automatisch.io',
|
||||
password: 'password',
|
||||
fullName: 'Initial admin'
|
||||
})
|
||||
.expect(204);
|
||||
|
||||
const user = await User.query().findOne({ email: 'user@automatisch.io' });
|
||||
|
||||
expect(user.roleId).toBe(adminRole.id);
|
||||
expect(await Config.isInstallationCompleted()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('for completed installations', () => {
|
||||
beforeEach(async () => {
|
||||
await createInstallationCompletedConfig();
|
||||
});
|
||||
|
||||
it('should respond with HTTP 403 when installation completed', async () => {
|
||||
expect(await Config.isInstallationCompleted()).toBe(true);
|
||||
|
||||
await request(app)
|
||||
.post('/api/v1/installation/users')
|
||||
.send({
|
||||
email: 'user@automatisch.io',
|
||||
password: 'password',
|
||||
fullName: 'Initial admin'
|
||||
})
|
||||
.expect(403);
|
||||
|
||||
const user = await User.query().findOne({ email: 'user@automatisch.io' });
|
||||
|
||||
expect(user).toBeUndefined();
|
||||
expect(await Config.isInstallationCompleted()).toBe(true);
|
||||
});
|
||||
})
|
||||
});
|
9
packages/backend/src/helpers/authorize-installation.js
Normal file
9
packages/backend/src/helpers/authorize-installation.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import Config from '../models/config.js';
|
||||
|
||||
export async function authorizeInstallation(request, response, next) {
|
||||
if (await Config.isInstallationCompleted()) {
|
||||
return response.status(403).end();
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
@@ -13,6 +13,28 @@ class Config extends Base {
|
||||
value: { type: 'object' },
|
||||
},
|
||||
};
|
||||
|
||||
static async isInstallationCompleted() {
|
||||
const installationCompletedEntry = await this
|
||||
.query()
|
||||
.where({
|
||||
key: 'installation.completed'
|
||||
})
|
||||
.first();
|
||||
|
||||
const installationCompleted = installationCompletedEntry?.value?.data === true;
|
||||
|
||||
return installationCompleted;
|
||||
}
|
||||
|
||||
static async markInstallationCompleted() {
|
||||
return await this.query().insert({
|
||||
key: 'installation.completed',
|
||||
value: {
|
||||
data: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Config;
|
||||
|
@@ -45,6 +45,10 @@ class Role extends Base {
|
||||
get isAdmin() {
|
||||
return this.key === 'admin';
|
||||
}
|
||||
|
||||
static async findAdmin() {
|
||||
return await this.query().findOne({ key: 'admin' });
|
||||
}
|
||||
}
|
||||
|
||||
export default Role;
|
||||
|
@@ -373,6 +373,19 @@ class User extends Base {
|
||||
return apps;
|
||||
}
|
||||
|
||||
static async createAdminUser({ email, password, fullName }) {
|
||||
const adminRole = await Role.findAdmin();
|
||||
|
||||
const adminUser = await this.query().insert({
|
||||
email,
|
||||
password,
|
||||
fullName,
|
||||
roleId: adminRole.id
|
||||
});
|
||||
|
||||
return adminUser;
|
||||
}
|
||||
|
||||
async $beforeInsert(queryContext) {
|
||||
await super.$beforeInsert(queryContext);
|
||||
|
||||
|
14
packages/backend/src/routes/api/v1/installation/users.js
Normal file
14
packages/backend/src/routes/api/v1/installation/users.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Router } from 'express';
|
||||
import asyncHandler from 'express-async-handler';
|
||||
import { authorizeInstallation } from '../../../../helpers/authorize-installation.js';
|
||||
import createUserAction from '../../../../controllers/api/v1/installation/users/create-user.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
'/',
|
||||
authorizeInstallation,
|
||||
asyncHandler(createUserAction)
|
||||
);
|
||||
|
||||
export default router;
|
@@ -18,6 +18,7 @@ import adminSamlAuthProvidersRouter from './api/v1/admin/saml-auth-providers.ee.
|
||||
import rolesRouter from './api/v1/admin/roles.ee.js';
|
||||
import permissionsRouter from './api/v1/admin/permissions.ee.js';
|
||||
import adminUsersRouter from './api/v1/admin/users.ee.js';
|
||||
import installationUsersRouter from './api/v1/installation/users.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -40,5 +41,7 @@ router.use('/api/v1/admin/users', adminUsersRouter);
|
||||
router.use('/api/v1/admin/roles', rolesRouter);
|
||||
router.use('/api/v1/admin/permissions', permissionsRouter);
|
||||
router.use('/api/v1/admin/saml-auth-providers', adminSamlAuthProvidersRouter);
|
||||
router.use('/api/v1/installation/users', installationUsersRouter);
|
||||
|
||||
|
||||
export default router;
|
||||
|
@@ -11,3 +11,7 @@ export const createConfig = async (params = {}) => {
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
export const createInstallationCompletedConfig = async () => {
|
||||
return await createConfig({ key: 'installation.completed', value: { data: true } });
|
||||
}
|
||||
|
Reference in New Issue
Block a user