Skip to content

Commit 828e8c4

Browse files
committed
fix: checkRepoInAuthorisedList push failure on URL mismatch
1 parent c7d94a9 commit 828e8c4

File tree

10 files changed

+211
-53
lines changed

10 files changed

+211
-53
lines changed

src/db/file/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const {
1717
export const {
1818
getRepos,
1919
getRepo,
20+
getRepoByUrl,
2021
createRepo,
2122
addUserCanPush,
2223
addUserCanAuthorise,

src/db/file/repo.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ if (!fs.existsSync('./.data')) fs.mkdirSync('./.data');
1010
/* istanbul ignore if */
1111
if (!fs.existsSync('./.data/db')) fs.mkdirSync('./.data/db');
1212

13-
const db = new Datastore({ filename: './.data/db/repos.db', autoload: true });
13+
// export for testing purposes
14+
export const db = new Datastore({ filename: './.data/db/repos.db', autoload: true });
1415
db.ensureIndex({ fieldName: 'name', unique: false });
1516
db.setAutocompactionInterval(COMPACTION_INTERVAL);
1617

@@ -49,6 +50,20 @@ export const getRepo = async (name: string) => {
4950
});
5051
};
5152

53+
export const getRepoByUrl = async (url: string) => {
54+
return new Promise<Repo | null>((resolve, reject) => {
55+
db.findOne({ url: url.toLowerCase().replace('.git', '') }, (err: Error | null, doc: Repo) => {
56+
// ignore for code coverage as neDB rarely returns errors even for an invalid query
57+
/* istanbul ignore if */
58+
if (err) {
59+
reject(err);
60+
} else {
61+
resolve(doc);
62+
}
63+
});
64+
});
65+
};
66+
5267
export const createRepo = async (repo: Repo) => {
5368
if (isBlank(repo.project)) {
5469
throw new Error('Project name cannot be empty');

src/db/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export const {
7171
updateUser,
7272
getRepos,
7373
getRepo,
74+
getRepoByUrl,
7475
createRepo,
7576
addUserCanPush,
7677
addUserCanAuthorise,

src/db/mongo/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const {
2020
export const {
2121
getRepos,
2222
getRepo,
23+
getRepoByUrl,
2324
createRepo,
2425
addUserCanPush,
2526
addUserCanAuthorise,

src/db/mongo/repo.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ export const getRepo = async (name: string) => {
1818
return collection.findOne({ name: { $eq: name } });
1919
};
2020

21+
export const getRepoByUrl = async (url: string) => {
22+
url = url.toLowerCase().replace('.git', '');
23+
const collection = await connect(collectionName);
24+
return collection.findOne({ url: { $eq: url } });
25+
};
26+
2127
export const createRepo = async (repo: Repo) => {
2228
repo.name = repo.name.toLowerCase();
2329
console.log(`creating new repo ${JSON.stringify(repo)}`);

src/proxy/processors/push-action/checkRepoInAuthorisedList.ts

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,22 @@
11
import { Action, Step } from '../../actions';
2-
import { getRepos } from '../../../db';
3-
import { Repo } from '../../../db/types';
4-
import { trimTrailingDotGit } from '../../../db/helper';
2+
import { getRepoByUrl } from '../../../db';
53

64
// Execute if the repo is approved
75
const exec = async (
86
req: any,
97
action: Action,
10-
authorisedList: () => Promise<Repo[]> = getRepos,
118
): Promise<Action> => {
129
const step = new Step('checkRepoInAuthorisedList');
1310

14-
const list = await authorisedList();
15-
console.log(list);
16-
17-
const found = list.find((x: Repo) => {
18-
const targetName = trimTrailingDotGit(action.repo.toLowerCase());
19-
const allowedName = trimTrailingDotGit(`${x.project}/${x.name}`.toLowerCase());
20-
console.log(`${targetName} = ${allowedName}`);
21-
return targetName === allowedName;
22-
});
23-
24-
console.log(found);
25-
26-
if (!found) {
27-
console.log('not found');
11+
const found = await getRepoByUrl(action.url);
12+
if (found) {
13+
step.log(`repo ${action.repo} is in the authorisedList`);
14+
} else {
2815
step.error = true;
29-
step.log(`repo ${action.repo} is not in the authorisedList, ending`);
30-
console.log('setting error');
31-
step.setError(`Rejecting repo ${action.repo} not in the authorisedList`);
32-
action.addStep(step);
33-
return action;
16+
step.log(`repo ${action.repo} is not in the authorised whitelist, ending`);
17+
step.setError(`Rejecting repo ${action.repo} not in the authorised whitelist`);
3418
}
3519

36-
console.log('found');
37-
step.log(`repo ${action.repo} is in the authorisedList`);
3820
action.addStep(step);
3921
return action;
4022
};

test/db/file/repo.test.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const { expect } = require('chai');
2+
const sinon = require('sinon');
3+
const repoModule = require('../../../src/db/file/repo');
4+
5+
describe('File DB', () => {
6+
let sandbox;
7+
8+
beforeEach(() => {
9+
sandbox = sinon.createSandbox();
10+
});
11+
12+
afterEach(() => {
13+
sandbox.restore();
14+
});
15+
16+
describe('getRepo', () => {
17+
it('should get the repo using the name', async () => {
18+
const repoData = {
19+
name: 'sample',
20+
users: { canPush: [] },
21+
url: 'http://example.com/sample-repo',
22+
};
23+
24+
sandbox.stub(repoModule.db, 'findOne').callsFake((query, cb) => cb(null, repoData));
25+
26+
const result = await repoModule.getRepo('Sample');
27+
expect(result).to.equal(repoData);
28+
});
29+
});
30+
31+
describe('getRepoByUrl', () => {
32+
it('should get the repo using the url', async () => {
33+
const repoData = {
34+
name: 'sample',
35+
users: { canPush: [] },
36+
url: 'https://github.com/finos/git-proxy',
37+
};
38+
39+
sandbox.stub(repoModule.db, 'findOne').callsFake((query, cb) => cb(null, repoData));
40+
41+
const result = await repoModule.getRepoByUrl('https://github.com/finos/git-proxy');
42+
expect(result).to.equal(repoData);
43+
});
44+
45+
it('should get the repo using the url, stripping off the .git', async () => {
46+
const repoData = {
47+
name: 'sample',
48+
users: { canPush: [] },
49+
url: 'https://github.com/finos/git-proxy',
50+
};
51+
52+
sandbox.stub(repoModule.db, 'findOne').callsFake((query, cb) => cb(null, repoData));
53+
54+
const result = await repoModule.getRepoByUrl('https://github.com/finos/git-proxy.git');
55+
56+
expect(repoModule.db.findOne.calledWith(sinon.match({ url: 'https://github.com/finos/git-proxy'}))).to.be.true;
57+
expect(result).to.equal(repoData);
58+
});
59+
});
60+
});

test/db/mongo/repo.test.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
const { expect } = require('chai');
2+
const sinon = require('sinon');
3+
const proxyqquire = require('proxyquire');
4+
5+
const repoCollection = {
6+
findOne: sinon.stub(),
7+
};
8+
9+
const connectionStub = sinon.stub().returns(repoCollection);
10+
11+
const { getRepo, getRepoByUrl } = proxyqquire('../../../src/db/mongo/repo', {
12+
'./helper': { connect: connectionStub },
13+
});
14+
15+
describe('MongoDB', () => {
16+
afterEach(function () {
17+
sinon.restore();
18+
});
19+
20+
describe('getRepo', () => {
21+
it('should get the repo using the name', async () => {
22+
const repoData = {
23+
name: 'sample',
24+
users: { canPush: [] },
25+
url: 'http://example.com/sample-repo',
26+
};
27+
repoCollection.findOne.resolves(repoData);
28+
29+
const result = await getRepo('Sample');
30+
expect(result).to.equal(repoData);
31+
expect(connectionStub.calledWith('repos')).to.be.true;
32+
expect(repoCollection.findOne.calledWith({ name: { $eq: 'sample' } })).to.be.true;
33+
});
34+
});
35+
36+
describe('getRepoByUrl', () => {
37+
it('should get the repo using the url', async () => {
38+
const repoData = {
39+
name: 'sample',
40+
users: { canPush: [] },
41+
url: 'https://github.com/finos/git-proxy',
42+
};
43+
repoCollection.findOne.resolves(repoData);
44+
45+
const result = await getRepoByUrl('https://github.com/finos/git-proxy');
46+
expect(result).to.equal(repoData);
47+
expect(connectionStub.calledWith('repos')).to.be.true;
48+
expect(
49+
repoCollection.findOne.calledWith({ url: { $eq: 'https://github.com/finos/git-proxy' } }),
50+
).to.be.true;
51+
});
52+
53+
it('should get the repo using the url, stripping off the .git', async () => {
54+
const repoData = {
55+
name: 'sample',
56+
users: { canPush: [] },
57+
url: 'https://github.com/finos/git-proxy',
58+
};
59+
repoCollection.findOne.resolves(repoData);
60+
61+
const result = await getRepoByUrl('https://github.com/finos/git-proxy.git');
62+
expect(result).to.equal(repoData);
63+
expect(connectionStub.calledWith('repos')).to.be.true;
64+
expect(
65+
repoCollection.findOne.calledWith({ url: { $eq: 'https://github.com/finos/git-proxy' } }),
66+
).to.be.true;
67+
});
68+
69+
it('should get the repo using the url, ignoring the case', async () => {
70+
const repoData = {
71+
name: 'sample',
72+
users: { canPush: [] },
73+
url: 'https://github.com/finos/git-proxy',
74+
};
75+
repoCollection.findOne.resolves(repoData);
76+
77+
const result = await getRepoByUrl('https://github.com/Finos/Git-Proxy.git');
78+
expect(result).to.equal(repoData);
79+
expect(connectionStub.calledWith('repos')).to.be.true;
80+
expect(
81+
repoCollection.findOne.calledWith({ url: { $eq: 'https://github.com/finos/git-proxy' } }),
82+
).to.be.true;
83+
});
84+
});
85+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const chai = require('chai');
2+
const sinon = require('sinon');
3+
const actions = require('../../src/proxy/actions/Action');
4+
const processor = require('../../src/proxy/processors/push-action/checkRepoInAuthorisedList');
5+
const expect = chai.expect;
6+
const db = require('../../src/db');
7+
8+
describe('Check a Repo is in the authorised list', async () => {
9+
afterEach(() => {
10+
sinon.restore();
11+
});
12+
13+
it('accepts the action if the repository is whitelisted in the db', async () => {
14+
sinon.stub(db, 'getRepoByUrl').resolves({
15+
name: 'repo-is-ok',
16+
project: 'thisproject',
17+
url: 'https://github.com/thisproject/repo-is-ok',
18+
});
19+
20+
const action = new actions.Action('123', 'type', 'get', 1234, 'thisproject/repo-is-ok');
21+
const result = await processor.exec(null, action);
22+
expect(result.error).to.be.false;
23+
expect(result.steps[0].logs[0]).to.eq('checkRepoInAuthorisedList - repo thisproject/repo-is-ok is in the authorisedList');
24+
});
25+
26+
it('rejects the action if repository not in the db', async () => {
27+
sinon.stub(db, 'getRepoByUrl').resolves(null);
28+
29+
const action = new actions.Action('123', 'type', 'get', 1234, 'thisproject/repo-is-not-ok');
30+
const result = await processor.exec(null, action);
31+
expect(result.error).to.be.true;
32+
expect(result.steps[0].logs[0]).to.eq('checkRepoInAuthorisedList - repo thisproject/repo-is-not-ok is not in the authorised whitelist, ending');
33+
});
34+
});

test/testCheckRepoInAuthList.test.js

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)