Skip to content
24 changes: 19 additions & 5 deletions packages/git-proxy-cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,9 @@ async function authoriseGitPush(id: string) {
/**
* Reject git push by ID
* @param {string} id The ID of the git push to reject
* @param {string} reason The reason for rejecting the push
*/
async function rejectGitPush(id: string) {
async function rejectGitPush(id: string, reason: string) {
if (!fs.existsSync(GIT_PROXY_COOKIE_FILE)) {
console.error('Error: Reject: Authentication required');
process.exitCode = 1;
Expand All @@ -247,7 +248,11 @@ async function rejectGitPush(id: string) {

await axios.post(
`${baseUrl}/api/v1/push/${id}/reject`,
{},
{
params: {
reason,
},
},
{
headers: { Cookie: cookies },
},
Expand All @@ -261,13 +266,17 @@ async function rejectGitPush(id: string) {

if (error.response) {
switch (error.response.status) {
case 400:
errorMessage = `Error: Reject: ${error.response.data.message}`;
process.exitCode = 3;
break;
case 401:
errorMessage = 'Error: Reject: Authentication required';
process.exitCode = 3;
process.exitCode = 4;
break;
case 404:
errorMessage = `Error: Reject: ID: '${id}': Not Found`;
process.exitCode = 4;
process.exitCode = 5;
}
}
console.error(errorMessage);
Expand Down Expand Up @@ -553,9 +562,14 @@ yargs(hideBin(process.argv)) // eslint-disable-line @typescript-eslint/no-unused
demandOption: true,
type: 'string',
},
reason: {
describe: 'Reason for rejecting the push',
demandOption: true,
type: 'string',
},
},
handler(argv) {
rejectGitPush(argv.id);
rejectGitPush(argv.id, argv.reason);
},
})
.command({
Expand Down
14 changes: 7 additions & 7 deletions packages/git-proxy-cli/test/testCli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ describe('test git-proxy-cli', function () {
}

const id = GHOST_PUSH_ID;
const cli = `${CLI_PATH} reject --id ${id}`;
const cli = `${CLI_PATH} reject --id ${id} --reason "Test rejection"`;
const expectedExitCode = 2;
const expectedMessages = null;
const expectedErrorMessages = ['Error: Reject:'];
Expand All @@ -451,7 +451,7 @@ describe('test git-proxy-cli', function () {
await helper.removeCookiesFile();

const id = GHOST_PUSH_ID;
const cli = `${CLI_PATH} reject --id ${id}`;
const cli = `${CLI_PATH} reject --id ${id} --reason "Test rejection"`;
const expectedExitCode = 1;
const expectedMessages = null;
const expectedErrorMessages = ['Error: Reject: Authentication required'];
Expand All @@ -463,8 +463,8 @@ describe('test git-proxy-cli', function () {
await helper.createCookiesFileWithExpiredCookie();
await helper.startServer();
const id = pushId;
const cli = `${CLI_PATH} reject --id ${id}`;
const expectedExitCode = 3;
const cli = `${CLI_PATH} reject --id ${id} --reason "Test rejection"`;
const expectedExitCode = 4;
const expectedMessages = null;
const expectedErrorMessages = ['Error: Reject: Authentication required'];
await helper.runCli(cli, expectedExitCode, expectedMessages, expectedErrorMessages);
Expand All @@ -479,8 +479,8 @@ describe('test git-proxy-cli', function () {
await helper.runCli(`${CLI_PATH} login --username admin --password admin`);

const id = GHOST_PUSH_ID;
const cli = `${CLI_PATH} reject --id ${id}`;
const expectedExitCode = 4;
const cli = `${CLI_PATH} reject --id ${id} --reason "Test rejection"`;
const expectedExitCode = 5;
const expectedMessages = null;
const expectedErrorMessages = [`Error: Reject: ID: '${id}': Not Found`];
await helper.runCli(cli, expectedExitCode, expectedMessages, expectedErrorMessages);
Expand Down Expand Up @@ -756,7 +756,7 @@ describe('test git-proxy-cli', function () {
let expectedErrorMessages = null;
await helper.runCli(cli, expectedExitCode, expectedMessages, expectedErrorMessages);

cli = `${CLI_PATH} reject --id ${pushId}`;
cli = `${CLI_PATH} reject --id ${pushId} --reason "Test rejection"`;
expectedExitCode = 0;
expectedMessages = [`Reject: ID: '${pushId}': OK`];
expectedErrorMessages = null;
Expand Down
4 changes: 2 additions & 2 deletions src/db/file/pushes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export const authorise = async (id: string, attestation: any): Promise<{ message
return { message: `authorised ${id}` };
};

export const reject = async (id: string, attestation: any): Promise<{ message: string }> => {
export const reject = async (id: string, rejection: any): Promise<{ message: string }> => {
const action = await getPush(id);
if (!action) {
throw new Error(`push ${id} not found`);
Expand All @@ -121,7 +121,7 @@ export const reject = async (id: string, attestation: any): Promise<{ message: s
action.authorised = false;
action.canceled = false;
action.rejected = true;
action.attestation = attestation;
action.rejection = rejection;
await writeAudit(action);
return { message: `reject ${id}` };
};
Expand Down
4 changes: 2 additions & 2 deletions src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ export const deletePush = (id: string): Promise<void> => sink.deletePush(id);
export const authorise = (id: string, attestation: any): Promise<{ message: string }> =>
sink.authorise(id, attestation);
export const cancel = (id: string): Promise<{ message: string }> => sink.cancel(id);
export const reject = (id: string, attestation: any): Promise<{ message: string }> =>
sink.reject(id, attestation);
export const reject = (id: string, rejection: any): Promise<{ message: string }> =>
sink.reject(id, rejection);
export const getRepos = (query?: Partial<RepoQuery>): Promise<Repo[]> => sink.getRepos(query);
export const getRepo = (name: string): Promise<Repo | null> => sink.getRepo(name);
export const getRepoByUrl = (url: string): Promise<Repo | null> => sink.getRepoByUrl(url);
Expand Down
4 changes: 2 additions & 2 deletions src/db/mongo/pushes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const writeAudit = async (action: Action): Promise<void> => {
await collection.updateOne({ id: data.id }, { $set: data }, options);
};

export const authorise = async (id: string, attestation: any): Promise<{ message: string }> => {
export const authorise = async (id: string, rejection: any): Promise<{ message: string }> => {
const action = await getPush(id);
if (!action) {
throw new Error(`push ${id} not found`);
Expand All @@ -72,7 +72,7 @@ export const authorise = async (id: string, attestation: any): Promise<{ message
action.authorised = true;
action.canceled = false;
action.rejected = false;
action.attestation = attestation;
action.rejection = rejection;
await writeAudit(action);
return { message: `authorised ${id}` };
};
Expand Down
2 changes: 1 addition & 1 deletion src/db/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export interface Sink {
deletePush: (id: string) => Promise<void>;
authorise: (id: string, attestation: any) => Promise<{ message: string }>;
cancel: (id: string) => Promise<{ message: string }>;
reject: (id: string, attestation: any) => Promise<{ message: string }>;
reject: (id: string, rejection: any) => Promise<{ message: string }>;
getRepos: (query?: Partial<RepoQuery>) => Promise<Repo[]>;
getRepo: (name: string) => Promise<Repo | null>;
getRepoByUrl: (url: string) => Promise<Repo | null>;
Expand Down
3 changes: 2 additions & 1 deletion src/proxy/actions/Action.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { processGitURLForNameAndOrg, processUrlPath } from '../routes/helper';
import { Step } from './Step';
import { Attestation, CommitData } from '../processors/types';
import { Attestation, CommitData, Rejection } from '../processors/types';

/**
* Class representing a Push.
Expand Down Expand Up @@ -34,6 +34,7 @@ class Action {
user?: string;
userEmail?: string;
attestation?: Attestation;
rejection?: Rejection;
lastStep?: Step;
proxyGitPath?: string;
newIdxFiles?: string[];
Expand Down
8 changes: 8 additions & 0 deletions src/proxy/processors/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ export type Attestation = {
questions: Question[];
};

export type Rejection = {
reviewer: {
username: string;
};
timestamp: string | Date;
reason: string;
};

export type CommitContent = {
item: number;
type: number;
Expand Down
32 changes: 30 additions & 2 deletions src/service/routes/push.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import express, { Request, Response } from 'express';
import * as db from '../../db';
import { PushQuery } from '../../db/types';
import { convertIgnorePatternToMinimatch } from '@eslint/compat';

const router = express.Router();

Expand Down Expand Up @@ -43,6 +44,14 @@ router.post('/:id/reject', async (req: Request, res: Response) => {
return;
}

const reason = req.body.params?.reason;
if (!reason || reason.trim().length === 0) {
res.status(400).send({
message: 'Rejection reason is required',
});
return;
}

const id = req.params.id;
const { username } = req.user as { username: string };

Expand Down Expand Up @@ -72,8 +81,27 @@ router.post('/:id/reject', async (req: Request, res: Response) => {
console.log({ isAllowed });

if (isAllowed) {
const result = await db.reject(id, null);
console.log(`user ${username} rejected push request for ${id}`);
console.log(`user ${username} rejected push request for ${id} with reason: ${reason}`);

const reviewerList = await db.getUsers({ username });
const reviewerEmail = reviewerList[0].email;

if (!reviewerEmail) {
res.status(401).send({
message: `There was no registered email address for the reviewer: ${username}`,
});
return;
}

const rejection = {
reason,
timestamp: new Date(),
reviewer: {
username,
reviewerEmail,
},
};
const result = await db.reject(id, rejection);
res.send(result);
} else {
res.status(401).send({
Expand Down
41 changes: 15 additions & 26 deletions src/ui/services/git-push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,48 +68,37 @@ const getPushes = async (
const authorisePush = async (
id: string,
setMessage: (message: string) => void,
setUserAllowedToApprove: (userAllowedToApprove: boolean) => void,
attestation: Array<{ label: string; checked: boolean }>,
): Promise<void> => {
): Promise<boolean> => {
const url = `${API_V1_BASE}/push/${id}/authorise`;
let errorMsg = '';
let isUserAllowedToApprove = true;
await axios
.post(
url,
{
params: {
attestation,
},
},
getAxiosConfig(),
)
.catch((error: any) => {
if (error.response && error.response.status === 401) {
errorMsg = 'You are not authorised to approve...';
isUserAllowedToApprove = false;
}
});
let success = true;
await axios.post(url, { params: { attestation } }, getAxiosConfig()).catch((error: any) => {
if (error.response && error.response.status === 401) {
errorMsg = 'You are not authorised to approve...';
success = false;
}
});
setMessage(errorMsg);
setUserAllowedToApprove(isUserAllowedToApprove);
return success;
};

const rejectPush = async (
id: string,
setMessage: (message: string) => void,
setUserAllowedToReject: (userAllowedToReject: boolean) => void,
): Promise<void> => {
rejection: { reason: string },
): Promise<boolean> => {
const url = `${API_V1_BASE}/push/${id}/reject`;
let errorMsg = '';
let isUserAllowedToReject = true;
await axios.post(url, {}, getAxiosConfig()).catch((error: any) => {
let success = true;
await axios.post(url, { params: rejection }, getAxiosConfig()).catch((error: any) => {
if (error.response && error.response.status === 401) {
errorMsg = 'You are not authorised to reject...';
isUserAllowedToReject = false;
success = false;
}
});
setMessage(errorMsg);
setUserAllowedToReject(isUserAllowedToReject);
return success;
};

const cancelPush = async (
Expand Down
Loading
Loading