Skip to content

Commit

Permalink
feat: add gitlab support
Browse files Browse the repository at this point in the history
  • Loading branch information
msagi committed May 4, 2024
1 parent 734d91e commit a0e4d64
Show file tree
Hide file tree
Showing 25 changed files with 366 additions and 169 deletions.
17 changes: 16 additions & 1 deletion config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
"description": "Configuration for customizing git-proxy",
"type": "object",
"properties": {
"proxyUrl": { "type": "string" },
"proxyConfig": {
"description": "Proxy configuration",
"type": "array",
"items": {
"$ref": "#/definitions/proxy"
}
},
"cookieSecret": { "type": "string" },
"sessionMaxAgeHours": { "type": "number" },
"api": {
Expand Down Expand Up @@ -70,6 +76,15 @@
}
},
"definitions": {
"proxy": {
"type": "object",
"properties": {
"path": { "type": "string"},
"url": { "type": "string"},
"enabled": { "type": "boolean" }
},
"required": ["path", "url", "enabled"]
},
"authorisedRepo": {
"type": "object",
"properties": {
Expand Down
17 changes: 8 additions & 9 deletions packages/git-proxy-cli/test/testCli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const TEST_REPO_CONFIG = {
name: 'git-proxy-test',
url: 'https://github.com/finos/git-proxy-test.git'
}
const TEST_REPO = 'finos/git-proxy-test.git';

describe('test git-proxy-cli', function () {
// *** help ***
Expand Down Expand Up @@ -291,7 +290,7 @@ describe('test git-proxy-cli', function () {

before(async function() {
await helper.addRepoToDb(TEST_REPO_CONFIG);
await helper.addGitPushToDb(pushId, TEST_REPO);
await helper.addGitPushToDb(pushId, TEST_REPO_CONFIG.url);
})

it('attempt to authorise should fail when server is down', async function () {
Expand Down Expand Up @@ -391,7 +390,7 @@ describe('test git-proxy-cli', function () {

before(async function() {
await helper.addRepoToDb(TEST_REPO_CONFIG);
await helper.addGitPushToDb(pushId, TEST_REPO);
await helper.addGitPushToDb(pushId, TEST_REPO_CONFIG.url);
})

it('attempt to cancel should fail when server is down', async function () {
Expand Down Expand Up @@ -552,7 +551,7 @@ describe('test git-proxy-cli', function () {

before(async function() {
await helper.addRepoToDb(TEST_REPO_CONFIG);
await helper.addGitPushToDb(pushId, TEST_REPO);
await helper.addGitPushToDb(pushId, TEST_REPO_CONFIG.url);
})

it('attempt to reject should fail when server is down', async function () {
Expand Down Expand Up @@ -648,7 +647,7 @@ describe('test git-proxy-cli', function () {

before(async function () {
await helper.addRepoToDb(TEST_REPO_CONFIG);
await helper.addGitPushToDb(pushId, TEST_REPO);
await helper.addGitPushToDb(pushId, TEST_REPO_CONFIG.url);
});

it('attempt to ls should list existing push', async function () {
Expand All @@ -662,7 +661,7 @@ describe('test git-proxy-cli', function () {
const expectedExitCode = 0;
const expectedMessages = [
pushId,
TEST_REPO,
TEST_REPO_CONFIG.url,
'authorised: false',
'blocked: true',
'canceled: false',
Expand Down Expand Up @@ -800,7 +799,7 @@ describe('test git-proxy-cli', function () {

cli = `npx -- @finos/git-proxy-cli ls --authorised true --canceled false --rejected false`;
expectedExitCode = 0;
expectedMessages = [pushId, TEST_REPO];
expectedMessages = [pushId, TEST_REPO_CONFIG.url];
expectedErrorMessages = null;
await helper.runCli(
cli,
Expand Down Expand Up @@ -844,7 +843,7 @@ describe('test git-proxy-cli', function () {

cli = `npx -- @finos/git-proxy-cli ls --authorised false --canceled false --rejected true`;
expectedExitCode = 0;
expectedMessages = [pushId, TEST_REPO];
expectedMessages = [pushId, TEST_REPO_CONFIG.url];
expectedErrorMessages = null;
await helper.runCli(
cli,
Expand Down Expand Up @@ -888,7 +887,7 @@ describe('test git-proxy-cli', function () {

cli = `npx -- @finos/git-proxy-cli ls --authorised false --canceled true --rejected false`;
expectedExitCode = 0;
expectedMessages = [pushId, TEST_REPO];
expectedMessages = [pushId, TEST_REPO_CONFIG.url];
expectedErrorMessages = null;
await helper.runCli(
cli,
Expand Down
7 changes: 4 additions & 3 deletions packages/git-proxy-cli/test/testCliUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const actions = require('../../../src/proxy/actions/Action');
const steps = require('../../../src/proxy/actions/Step');
const processor = require('../../../src/proxy/processors/push-action/audit');
const db = require('../../../src/db');
const { Repo } = require('../../../src/model');

// cookie file name
const GIT_PROXY_COOKIE_FILE = 'git-proxy-cookie';
Expand Down Expand Up @@ -167,16 +168,16 @@ async function addRepoToDb(newRepo, debug = false) {
/**
* Add a new git push record to the database.
* @param {string} id The ID of the git push.
* @param {string} repo The repository of the git push.
* @param {string} repoUrl The repository url of the git push.
* @param {boolean} debug Flag to enable logging for debugging.
*/
async function addGitPushToDb(id, repo, debug = false) {
async function addGitPushToDb(id, repoUrl, debug = false) {
const action = new actions.Action(
id,
'push', // type
'get', // method
Date.now(), // timestamp
repo,
new Repo(repoUrl),
);
const step = new steps.Step(
'authBlock', // stepName
Expand Down
13 changes: 12 additions & 1 deletion proxy.config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
{
"proxyUrl": "https://github.com",
"proxyConfig": [
{
"path": "/github.com",
"url": "https://github.com",
"enabled": true
},
{
"path": "/gitlab.com",
"url": "https://gitlab.com",
"enabled": true
}
],
"cookieSecret": "cookie secret",
"sessionMaxAgeHours": 12,
"tempPassword": {
Expand Down
16 changes: 8 additions & 8 deletions src/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ let _authorisedList = defaultSettings.authorisedList;
let _database = defaultSettings.sink;
let _authentication = defaultSettings.authentication;
let _tempPassword = defaultSettings.tempPassword;
let _proxyUrl = defaultSettings.proxyUrl;
let _proxyConfig = defaultSettings.proxyConfig;
let _api = defaultSettings.api;
let _cookieSecret = defaultSettings.cookieSecret;
let _sessionMaxAgeHours = defaultSettings.sessionMaxAgeHours;
Expand All @@ -22,14 +22,14 @@ const _urlShortener = defaultSettings.urlShortener;
const _contactEmail = defaultSettings.contactEmail;
const _csrfProtection = defaultSettings.csrfProtection;

// Get configured proxy URL
const getProxyUrl = () => {
if (_userSettings !== null && _userSettings.proxyUrl) {
_proxyUrl = _userSettings.proxyUrl;
// Gets a list of configured proxies
const getProxyConfigList = () => {
if (_userSettings !== null && _userSettings.proxyConfig) {
_proxyConfig = _userSettings.proxyConfig;
}

return _proxyUrl;
};
return _proxyConfig;
}

// Gets a list of authorised repositories
const getAuthorisedList = () => {
Expand Down Expand Up @@ -141,7 +141,7 @@ const getCSRFProtection = () => {
};

exports.getAPIs = getAPIs;
exports.getProxyUrl = getProxyUrl;
exports.getProxyConfigList = getProxyConfigList;
exports.getAuthorisedList = getAuthorisedList;
exports.getDatabase = getDatabase;
exports.logConfiguration = logConfiguration;
Expand Down
4 changes: 2 additions & 2 deletions src/db/file/pushes.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ const cancel = async (id, logger) => {
const canUserCancelPush = async (id, user) => {
return new Promise(async (resolve) => {
const pushDetail = await getPush(id);
const repoName = pushDetail.repoName.replace('.git', '');
const isAllowed = await repo.isUserPushAllowed(repoName, user);
const repoUrl = pushDetail.repo.url;
const isAllowed = await repo.isUserPushAllowed(repoUrl, user);

if (isAllowed) {
resolve(true);
Expand Down
21 changes: 18 additions & 3 deletions src/db/file/repo.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ exports.getRepo = async (name) => {
});
};

exports.getRepoByUrl = async (url) => {
return new Promise((resolve, reject) => {
db.findOne({ url: url }, (err, doc) => {
if (err) {
reject(err);
} else {
if (!doc) {
resolve(null);
} else {
resolve(doc);
}
}
});
});
};

exports.createRepo = async (repo) => {
repo.users = {
canPush: [],
Expand Down Expand Up @@ -140,10 +156,9 @@ exports.deleteRepo = async (name) => {
});
};

exports.isUserPushAllowed = async (name, user) => {
name = name.toLowerCase();
exports.isUserPushAllowed = async (url, user) => {
return new Promise(async (resolve, reject) => {
const repo = await exports.getRepo(name);
const repo = await exports.getRepoByUrl(url);
console.log(repo.users.canPush);
console.log(repo.users.canAuthorise);

Expand Down
4 changes: 2 additions & 2 deletions src/db/mongo/pushes.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ const canUserApproveRejectPush = async (id, user) => {
const canUserCancelPush = async (id, user) => {
return new Promise(async (resolve) => {
const pushDetail = await getPush(id);
const repoName = pushDetail.repoName.replace('.git', '');
const isAllowed = await repo.isUserPushAllowed(repoName, user);
const repoUrl = pushDetail.repo.url;
const isAllowed = await repo.isUserPushAllowed(repoUrl, user);

if (isAllowed) {
resolve(true);
Expand Down
10 changes: 7 additions & 3 deletions src/db/mongo/repo.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ exports.getRepo = async (name) => {
return collection.findOne({ name: { $eq: name } });
};

exports.getRepoByUrl = async (url) => {
const collection = await connect(cnName);
return collection.findOne({ url: { $eq: url}});
}

exports.createRepo = async (repo) => {
console.log(`creating new repo ${JSON.stringify(repo)}`);

Expand Down Expand Up @@ -67,10 +72,9 @@ exports.deleteRepo = async (name) => {
await collection.deleteMany({ name: name });
};

exports.isUserPushAllowed = async (name, user) => {
name = name.toLowerCase();
exports.isUserPushAllowed = async (url, user) => {
return new Promise(async (resolve, reject) => {
const repo = await exports.getRepo(name);
const repo = await exports.getRepoByUrl(url);
console.log(repo.users.canPush);
console.log(repo.users.canAuthorise);

Expand Down
31 changes: 31 additions & 0 deletions src/model/Repo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Regex inspector - https://www.debuggex.com/
// eslint-disable-next-line no-useless-escape
const GIT_URL_REGEX = new RegExp("^(https)://(github\.com|gitlab\.com)/([a-zA-Z0-9\-\.]+)/([a-zA-Z0-9\-]+)(\.git)(/)?$");

/** Class representing a Repo. */
class Repo {
url;
protocol;
host;
project;
name;

/**
*
* @param {string} url The url for the repo.
*/
constructor(url) {
const parsedUrl = url?.match(GIT_URL_REGEX);
if (parsedUrl) {
this.url = url;
this.protocol = parsedUrl[1];
this.host = parsedUrl[2];
this.project = parsedUrl[3];
this.name = parsedUrl[4] + parsedUrl[5]; // repo name + .git
return;
}
throw new Error(`Invalid repo url: "${url}"`);
}
}

exports.Repo = Repo;
1 change: 1 addition & 0 deletions src/model/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.Repo = require('./Repo').Repo;
9 changes: 4 additions & 5 deletions src/proxy/actions/Action.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/** Class representing a Push. */
const config = require('../../config');

/**
* Create a new action
Expand Down Expand Up @@ -35,9 +34,9 @@ class Action {
this.type = type;
this.method = method;
this.timestamp = timestamp;
this.project = repo.split('/')[0];
this.repoName = repo.split('/')[1];
this.url = `${config.getProxyUrl()}/${repo}`;
this.project = repo.project;
this.repoName = repo.name;
this.url = repo.url;
this.repo = repo;
}

Expand Down Expand Up @@ -97,7 +96,7 @@ class Action {
}

/**
*`
*
*/
setAllowPush() {
this.allowPush = true;
Expand Down
34 changes: 25 additions & 9 deletions src/proxy/processors/pre-processor/parseAction.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const actions = require('../../actions');
const config = require('../../../config');
const { Repo } = require('../../../model');

const exec = async (req) => {
const id = Date.now();
const timestamp = id;
const repoName = getRepoNameFromUrl(req.originalUrl);
const repo = getRepoFromUrlPath(req.originalUrl);
const paths = req.originalUrl.split('/');

let type = 'default';
Expand All @@ -18,20 +20,34 @@ const exec = async (req) => {
) {
type = 'push';
}
return new actions.Action(id, type, req.method, timestamp, repoName);
return new actions.Action(id, type, req.method, timestamp, repo);
};

const getRepoNameFromUrl = (url) => {
const parts = url.split('/');
for (let i = 0, len = parts.length; i < len; i++) {
const part = parts[i];
if (part.endsWith('.git')) {
const repo = `${parts[i - 1]}/${part}`;
return repo.trim();
// Get repo from URL path
const getRepoFromUrlPath = (urlPath) => {
const urlPathSegments = urlPath.split('/');
const proxyPath = [ urlPathSegments[0], urlPathSegments[1]].join('/');
for (const proxyConfig of config.getProxyConfigList()) {
if (proxyConfig.path == proxyPath) {
// replace '/proxyPath' -> 'proxyUrl' from proxy config
urlPathSegments.shift(); // remove first '/' item
urlPathSegments[0] = proxyConfig.url; // replace proxy path with proxy url
// build repo url without git path
const repoUrlSegments = [];
for (const urlPathSegment of urlPathSegments) {
repoUrlSegments.push(urlPathSegment);
// eslint-disable-next-line no-useless-escape
if (urlPathSegment.match(/[a-zA-Z0-9\-]+\.git/)) {
break;
}
}
const repoUrl = repoUrlSegments.join('/');
return new Repo(repoUrl);
}
}
return 'NOT-FOUND';
};

exec.displayName = 'parseAction.exec';
exports.exec = exec;
exports.getRepoFromUrlPath = getRepoFromUrlPath;
Loading

0 comments on commit a0e4d64

Please sign in to comment.