Skip to content

Commit 0ffb79c

Browse files
authored
Merge branch 'main' into enforce-80-percent-coverage-ci
2 parents a9f36ce + 1232fa5 commit 0ffb79c

File tree

4 files changed

+408
-0
lines changed

4 files changed

+408
-0
lines changed

packages/git-proxy-cli/index.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,59 @@ async function reloadConfig() {
333333
}
334334
}
335335

336+
/**
337+
* Create a new user
338+
* @param {string} username The username for the new user
339+
* @param {string} password The password for the new user
340+
* @param {string} email The email for the new user
341+
* @param {string} gitAccount The git account for the new user
342+
* @param {boolean} [admin=false] Whether the user should be an admin (optional)
343+
*/
344+
async function createUser(username, password, email, gitAccount, admin = false) {
345+
if (!fs.existsSync(GIT_PROXY_COOKIE_FILE)) {
346+
console.error('Error: Create User: Authentication required');
347+
process.exitCode = 1;
348+
return;
349+
}
350+
351+
try {
352+
const cookies = JSON.parse(fs.readFileSync(GIT_PROXY_COOKIE_FILE, 'utf8'));
353+
354+
await axios.post(
355+
`${baseUrl}/api/auth/create-user`,
356+
{
357+
username,
358+
password,
359+
email,
360+
gitAccount,
361+
admin,
362+
},
363+
{
364+
headers: { Cookie: cookies },
365+
},
366+
);
367+
368+
console.log(`User '${username}' created successfully`);
369+
} catch (error) {
370+
let errorMessage = `Error: Create User: '${error.message}'`;
371+
process.exitCode = 2;
372+
373+
if (error.response) {
374+
switch (error.response.status) {
375+
case 401:
376+
errorMessage = 'Error: Create User: Authentication required';
377+
process.exitCode = 3;
378+
break;
379+
case 400:
380+
errorMessage = `Error: Create User: ${error.response.data.message}`;
381+
process.exitCode = 4;
382+
break;
383+
}
384+
}
385+
console.error(errorMessage);
386+
}
387+
}
388+
336389
// Parsing command line arguments
337390
yargs(hideBin(process.argv)) // eslint-disable-line @typescript-eslint/no-unused-expressions
338391
.command({
@@ -468,6 +521,41 @@ yargs(hideBin(process.argv)) // eslint-disable-line @typescript-eslint/no-unused
468521
description: 'Reload GitProxy configuration without restarting',
469522
action: reloadConfig,
470523
})
524+
.command({
525+
command: 'create-user',
526+
describe: 'Create a new user',
527+
builder: {
528+
username: {
529+
describe: 'Username for the new user',
530+
demandOption: true,
531+
type: 'string',
532+
},
533+
password: {
534+
describe: 'Password for the new user',
535+
demandOption: true,
536+
type: 'string',
537+
},
538+
email: {
539+
describe: 'Email for the new user',
540+
demandOption: true,
541+
type: 'string',
542+
},
543+
gitAccount: {
544+
describe: 'Git account for the new user',
545+
demandOption: true,
546+
type: 'string',
547+
},
548+
admin: {
549+
describe: 'Whether the user should be an admin (optional)',
550+
demandOption: false,
551+
type: 'boolean',
552+
default: false,
553+
},
554+
},
555+
handler(argv) {
556+
createUser(argv.username, argv.password, argv.email, argv.gitAccount, argv.admin);
557+
},
558+
})
471559
.demandCommand(1, 'You need at least one command before moving on')
472560
.strict()
473561
.help().argv;

packages/git-proxy-cli/test/testCli.test.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,136 @@ describe('test git-proxy-cli', function () {
490490
});
491491
});
492492

493+
// *** create user ***
494+
495+
describe('test git-proxy-cli :: create-user', function () {
496+
before(async function () {
497+
await helper.addUserToDb(TEST_USER, TEST_PASSWORD, TEST_EMAIL, TEST_GIT_ACCOUNT);
498+
});
499+
500+
after(async function () {
501+
await helper.removeUserFromDb(TEST_USER);
502+
});
503+
504+
it('attempt to create user should fail when server is down', async function () {
505+
try {
506+
// start server -> login -> stop server
507+
await helper.startServer(service);
508+
await helper.runCli(`npx -- @finos/git-proxy-cli login --username admin --password admin`);
509+
} finally {
510+
await helper.closeServer(service.httpServer);
511+
}
512+
513+
const cli = `npx -- @finos/git-proxy-cli create-user --username newuser --password newpass --email new@email.com --gitAccount newgit`;
514+
const expectedExitCode = 2;
515+
const expectedMessages = null;
516+
const expectedErrorMessages = ['Error: Create User:'];
517+
await helper.runCli(cli, expectedExitCode, expectedMessages, expectedErrorMessages);
518+
});
519+
520+
it('attempt to create user should fail when not authenticated', async function () {
521+
await helper.removeCookiesFile();
522+
523+
const cli = `npx -- @finos/git-proxy-cli create-user --username newuser --password newpass --email new@email.com --gitAccount newgit`;
524+
const expectedExitCode = 1;
525+
const expectedMessages = null;
526+
const expectedErrorMessages = ['Error: Create User: Authentication required'];
527+
await helper.runCli(cli, expectedExitCode, expectedMessages, expectedErrorMessages);
528+
});
529+
530+
it('attempt to create user should fail when not admin', async function () {
531+
try {
532+
await helper.startServer(service);
533+
await helper.runCli(
534+
`npx -- @finos/git-proxy-cli login --username testuser --password testpassword`,
535+
);
536+
537+
const cli = `npx -- @finos/git-proxy-cli create-user --username newuser --password newpass --email new@email.com --gitAccount newgit`;
538+
const expectedExitCode = 3;
539+
const expectedMessages = null;
540+
const expectedErrorMessages = ['Error: Create User: Authentication required'];
541+
await helper.runCli(cli, expectedExitCode, expectedMessages, expectedErrorMessages);
542+
} finally {
543+
await helper.closeServer(service.httpServer);
544+
}
545+
});
546+
547+
it('attempt to create user should fail with missing required fields', async function () {
548+
try {
549+
await helper.startServer(service);
550+
await helper.runCli(`npx -- @finos/git-proxy-cli login --username admin --password admin`);
551+
552+
const cli = `npx -- @finos/git-proxy-cli create-user --username newuser --password "" --email new@email.com --gitAccount newgit`;
553+
const expectedExitCode = 4;
554+
const expectedMessages = null;
555+
const expectedErrorMessages = ['Error: Create User: Missing required fields'];
556+
await helper.runCli(cli, expectedExitCode, expectedMessages, expectedErrorMessages);
557+
} finally {
558+
await helper.closeServer(service.httpServer);
559+
}
560+
});
561+
562+
it('should successfully create a new user', async function () {
563+
const uniqueUsername = `newuser_${Date.now()}`;
564+
try {
565+
await helper.startServer(service);
566+
await helper.runCli(`npx -- @finos/git-proxy-cli login --username admin --password admin`);
567+
568+
const cli = `npx -- @finos/git-proxy-cli create-user --username ${uniqueUsername} --password newpass --email new@email.com --gitAccount newgit`;
569+
const expectedExitCode = 0;
570+
const expectedMessages = [`User '${uniqueUsername}' created successfully`];
571+
const expectedErrorMessages = null;
572+
await helper.runCli(cli, expectedExitCode, expectedMessages, expectedErrorMessages);
573+
574+
// Verify we can login with the new user
575+
await helper.runCli(
576+
`npx -- @finos/git-proxy-cli login --username ${uniqueUsername} --password newpass`,
577+
0,
578+
[`Login "${uniqueUsername}" <new@email.com>: OK`],
579+
null,
580+
);
581+
} finally {
582+
await helper.closeServer(service.httpServer);
583+
// Clean up the created user
584+
try {
585+
await helper.removeUserFromDb(uniqueUsername);
586+
} catch (error) {
587+
// Ignore cleanup errors
588+
}
589+
}
590+
});
591+
592+
it('should successfully create a new admin user', async function () {
593+
const uniqueUsername = `newadmin_${Date.now()}`;
594+
try {
595+
await helper.startServer(service);
596+
await helper.runCli(`npx -- @finos/git-proxy-cli login --username admin --password admin`);
597+
598+
const cli = `npx -- @finos/git-proxy-cli create-user --username ${uniqueUsername} --password newpass --email ${uniqueUsername}@email.com --gitAccount newgit --admin`;
599+
const expectedExitCode = 0;
600+
const expectedMessages = [`User '${uniqueUsername}' created successfully`];
601+
const expectedErrorMessages = null;
602+
await helper.runCli(cli, expectedExitCode, expectedMessages, expectedErrorMessages);
603+
604+
// Verify we can login with the new admin user
605+
await helper.runCli(
606+
`npx -- @finos/git-proxy-cli login --username ${uniqueUsername} --password newpass`,
607+
0,
608+
[`Login "${uniqueUsername}" <${uniqueUsername}@email.com> (admin): OK`],
609+
null,
610+
);
611+
} finally {
612+
await helper.closeServer(service.httpServer);
613+
// Clean up the created user
614+
try {
615+
await helper.removeUserFromDb(uniqueUsername);
616+
} catch (error) {
617+
console.error('Error cleaning up user', error);
618+
}
619+
}
620+
});
621+
});
622+
493623
// *** tests require push in db ***
494624

495625
describe('test git-proxy-cli :: git push administration', function () {

src/service/routes/auth.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,37 @@ router.get('/me', async (req, res) => {
168168
}
169169
});
170170

171+
router.post('/create-user', async (req, res) => {
172+
if (!req.user || !req.user.admin) {
173+
return res.status(401).send({
174+
message: 'You are not authorized to perform this action...',
175+
});
176+
}
177+
178+
try {
179+
const { username, password, email, gitAccount, admin: isAdmin = false } = req.body;
180+
181+
if (!username || !password || !email || !gitAccount) {
182+
return res.status(400).send({
183+
message: 'Missing required fields: username, password, email, and gitAccount are required',
184+
});
185+
}
186+
187+
await db.createUser(username, password, email, gitAccount, isAdmin);
188+
res.status(201).send({
189+
message: 'User created successfully',
190+
username,
191+
});
192+
} catch (error) {
193+
console.error('Error creating user:', error);
194+
res.status(400).send({
195+
message: error.message || 'Failed to create user',
196+
});
197+
}
198+
});
199+
200+
module.exports = router;
201+
171202
module.exports = {
172203
router,
173204
loginSuccessHandler,

0 commit comments

Comments
 (0)