diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index dbdf48212..aca14ebf7 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node-version: [13.x] + node-version: [16.x] mongodb-version: [4.4] steps: diff --git a/README.md b/README.md index b83724f4b..aebc6d795 100644 --- a/README.md +++ b/README.md @@ -48,13 +48,42 @@ Git Proxy is built with a developer-first mindset. By presenting simple-to-follo To install Git Proxy, use the [npm](https://www.npmjs.com/) package manager: ```bash -$ npm install @finos/git-proxy +$ npm install -g @finos/git-proxy +$ git-proxy ``` To install a specific version of Git Proxy, append the version to the end of the `install` command: ```bash -$ npm install @finos/git-proxy@1.0.0 +$ npm install -g @finos/git-proxy@1.0.0 +``` + +You can also run directly using `npx`: + +```bash +$ npx @finos/git-proxy +``` + +## Configuration +By default, git-proxy ships with a [default configuration](./proxy.config.json) for demonstration purposes. In most environments, this should be overridden by your user-specific values. + +To set your own values, create a `proxy.config.json` in the current working directory. This will be loaded when you execute `git-proxy` if present. + +If you wish to specify a different file location to use as configuration, use the `-c/--config` command-line argument: + +```bash +$ git-proxy --config /etc/gitproxy/config.json +# With npx +$ npx -- git-proxy --config /etc/gitproxy/config.json +``` + +### Validation +To valid your configuration against the [included schema](config.schema.json), use the following included script: + +```bash +$ git-proxy --validate +# Check a custom file location +$ git-proxy --validate --config /etc/gitproxy/config.json ``` ## Contributing diff --git a/config.schema.json b/config.schema.json new file mode 100644 index 000000000..c1f2cc9fd --- /dev/null +++ b/config.schema.json @@ -0,0 +1,79 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://git-proxy.finos.org/config.schema.json", + "title": "Git Proxy", + "description": "Configuration file for modifying the behaviour of git-proxy", + "type": "object", + "properties": { + "authorisedList": { + "description": "List of repositories that are authorised to be pushed to through the proxy.", + "type": "array", + "items": { + "$ref": "#/$defs/authorisedRepo" + } + }, + "sink": { + "description": "List of database sources. The first source in the configuration with enabled=true will be used.", + "type": "array", + "items": { + "$ref": "#/$defs/database" + } + }, + "authentication": { + "description": "List of authentication sources. The first source in the configuration with enabled=true will be used.", + "type": "array", + "items": { + "$ref": "#/$defs/authentication" + } + }, + "tempPassword": { + "description": "Toggle the generation of temporary password for git-proxy admin user", + "type": "object", + "properties": { + "sendEmail": { "type": "boolean" }, + "emailConfig": { + "description": "Generic object to configure nodemailer. For full type information, please see https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/nodemailer", + "type": "object" + } + } + } + }, + "additionalProperties": false, + "anyOf": [ + { "required": "authorisedList" }, + { "required": "sink" }, + { "required": "authentication" }, + { "required": "tempPassword" } + ], + "$defs": { + "authorisedRepo": { + "type": "object", + "properties": { + "project": { "type": "string" }, + "name": { "type": "string" }, + "url": { "type": "string" } + }, + "required": [ "project", "name", "url" ] + }, + "database": { + "type": "object", + "properties": { + "type": { "type": "string" }, + "enabled": { "type": "boolean" }, + "connectionString": { "type": "string" }, + "options": { "type": "object" }, + "params": { "type": "object" } + }, + "required": [ "type", "enabled" ] + }, + "authentication": { + "type": "object", + "properties": { + "type": { "type": "string" }, + "enabled": { "type": "boolean" }, + "options": { "type": "object" } + }, + "required": [ "type", "enabled" ] + } + } +} \ No newline at end of file diff --git a/index.js b/index.js index 36bcfa570..6588bd626 100755 --- a/index.js +++ b/index.js @@ -1,4 +1,48 @@ #!/usr/bin/env node +/* eslint-disable max-len */ +const argv = require('yargs/yargs')(process.argv.slice(2)) + .usage('Usage: $0 [options]') + .options({ + validate: { + description: + 'Check the proxy.config.json file in the current working directory for validation errors.', + required: false, + alias: 'v', + }, + config: { + description: 'Path to custom git-proxy configuration file.', + default: 'proxy.config.json', + required: false, + alias: 'c', + }, + }).argv; + +require('./src/config/file').configFile = argv.c ? argv.c : undefined; + +if (argv.v) { + const fs = require('fs'); + const path = require('path'); + const validate = require('jsonschema').validate; + const configFile = require('./src/config/file').configFile; + + if (!fs.existsSync(configFile)) { + console.error( + `Config file ${configFile} doesn't exist, nothing to validate! Did you forget -c/--config?`, + ); + process.exit(1); + } + + const config = JSON.parse(fs.readFileSync(configFile)); + const schema = JSON.parse( + fs.readFileSync(path.join(__dirname, 'config.schema.json')), + ); + + validate(config, schema, { required: true, throwError: true }); + + console.log(`${configFile} is valid`); + process.exit(0); +} + const proxy = require('./src/proxy'); const service = require('./src/service'); diff --git a/package-lock.json b/package-lock.json index 16cd438b7..140145a7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@finos/git-proxy", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@finos/git-proxy", - "version": "1.0.0", + "version": "1.0.1", "license": "Apache-2.0", "dependencies": { "@material-ui/core": "^4.11.0", @@ -27,6 +27,7 @@ "express-session": "^1.17.1", "generate-password": "^1.5.1", "history": "5.3.0", + "jsonschema": "^1.4.1", "lodash": "^4.17.21", "moment": "^2.29.4", "mongodb": "^5.0.0", @@ -41,7 +42,11 @@ "react-dom": "^16.13.1", "react-html-parser": "^2.0.2", "react-router-dom": "6.16.0", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "git-proxy": "index.js" }, "devDependencies": { "@babel/eslint-parser": "^7.22.9", @@ -5662,6 +5667,14 @@ "node >= 0.2.0" ] }, + "node_modules/jsonschema": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "engines": { + "node": "*" + } + }, "node_modules/JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", diff --git a/package.json b/package.json index 304a105a1..18277b0d8 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "express-session": "^1.17.1", "generate-password": "^1.5.1", "history": "5.3.0", + "jsonschema": "^1.4.1", "lodash": "^4.17.21", "moment": "^2.29.4", "mongodb": "^5.0.0", @@ -51,7 +52,8 @@ "react-dom": "^16.13.1", "react-html-parser": "^2.0.2", "react-router-dom": "6.16.0", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "yargs": "^17.7.2" }, "devDependencies": { "@babel/eslint-parser": "^7.22.9", diff --git a/resources/config.json b/proxy.config.json similarity index 100% rename from resources/config.json rename to proxy.config.json diff --git a/resources/server.cert b/resources/server.cert deleted file mode 100644 index aeb2d089c..000000000 --- a/resources/server.cert +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDgzCCAmugAwIBAgIUSUNCW7+UIzWMqSsBswEdWxplmYYwDQYJKoZIhvcNAQEL -BQAwUTELMAkGA1UEBhMCdWsxDjAMBgNVBAgMBWJ1Y2tzMQ8wDQYDVQQHDAZtYXJs -b3cxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA0MTcx -NTEyMjhaFw0yMDA1MTcxNTEyMjhaMFExCzAJBgNVBAYTAnVrMQ4wDAYDVQQIDAVi -dWNrczEPMA0GA1UEBwwGbWFybG93MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRz -IFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/eVtu1NCJ -7gHa+OFopgvkm7PDGC2ShU7bbCJeXfv05aIY+Qgygm5dSRozUaeH4C+v8MgAnPnV -yCeK2P2CP4O1Kmw+GEEr0BJTLzzHOgigLJ5POkj/u70skPFz5UoF/i4kimVO1jS/ -jjZEihmssz9WAMg6XoYsNDLCUOETYL0KdPSFrN8Y7Qtwr/l53+1blVlPgrkTDJCz -zNcZqQWbW/wPEnTcuK/XT4jHX8h0trS5pKf6G3xP0rfnHcXJFdzIh5eVbg0svL0C -MnP0EyB+EeXDrda0/mxXYcmWx3nUqiJCJExR2+UeB2R/zwUFXAag7AMJh6BkK823 -bCs4WCViYMlTAgMBAAGjUzBRMB0GA1UdDgQWBBRHUkx9G3VBXY6CausRfrUG5dDs -uzAfBgNVHSMEGDAWgBRHUkx9G3VBXY6CausRfrUG5dDsuzAPBgNVHRMBAf8EBTAD -AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBpV25jdW4bgRRfux5eian/Etz02ZkLBC78 -mat1slV5GekzwiGdp6GkEYMsodx7mJOr1zDRSf5LBn+XeCeTCyS5wmTiQ9PqlDKI -ZoeLy3iSM7sain52BmVPZR9UcO+Ja+4f1LITnVnTHaalZR1Abb7nrtIO7hOeX8z4 -TOiQ1RExGFacSB/jIX3V62o8yOO2c48oFF6Ybjfu4ellmy1g0cygk5hhwoq4OtOK -gNMM+ThD862ox54hEsZLZQu1PpfRFWamgiJTAZG1Pw9uZLz7lja0mhmDWMpP6FDT -zaT6J3N4J97kMSCRLVUnmz9JNvkp26otxie67/91psEZnZA70BwP ------END CERTIFICATE----- \ No newline at end of file diff --git a/resources/server.key b/resources/server.key deleted file mode 100644 index b706c2a02..000000000 --- a/resources/server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC/eVtu1NCJ7gHa -+OFopgvkm7PDGC2ShU7bbCJeXfv05aIY+Qgygm5dSRozUaeH4C+v8MgAnPnVyCeK -2P2CP4O1Kmw+GEEr0BJTLzzHOgigLJ5POkj/u70skPFz5UoF/i4kimVO1jS/jjZE -ihmssz9WAMg6XoYsNDLCUOETYL0KdPSFrN8Y7Qtwr/l53+1blVlPgrkTDJCzzNcZ -qQWbW/wPEnTcuK/XT4jHX8h0trS5pKf6G3xP0rfnHcXJFdzIh5eVbg0svL0CMnP0 -EyB+EeXDrda0/mxXYcmWx3nUqiJCJExR2+UeB2R/zwUFXAag7AMJh6BkK823bCs4 -WCViYMlTAgMBAAECggEBAIXN4s1SvuCEiJtjLPIah1kcTcTaUo5/xhmkOWhEuVvs -VRiqfsX9S+64tSyDtVVIn0qOMtXq3NQ+aROoi4/HntytZrMF9BUP9J5Y9loq/fgg -9ghbrMO5iHtqVrEs3EvP0qMKa71qB7aNRPMkpsh/ApWxOjs/7vdZCter+X23LqPs -3Kq8bQtiOvXCUQyORHOEWbdiAMvCrpo7EfqKkFGP0xaau4+kSmRZKlnaA78Hd7am -YX+XaPnu3QTjmlHWAIgbc5zkJS0pwX0xN2qnQ02RLV9zxCdXu6x5W1VocCme+BNS -9Zae2aDLgVeYy4KrAYvWIzb1HXOk6l5hLjAS4IWRH3ECgYEA8JGwMVPDYHzjKRT+ -X4a5a7GleJjSddsuiNdvk6Tx252PLJyFWmbaWeom9OyKhZcre7F+5Ms4MgGaOI3B -vrD+hEsNwZYR2tLS3wsFhhLLTth5rB4xFpDkvztUJe3tqFah3pyD8jos+z/X6v20 -62ArJkegzvzQVNEc3JbpZKolyo0CgYEAy8F+ggO6OnPr5liFNJvfYDJ20BdKg67D -mXRRG3/X+/wM71DH1Mr7GUVS3ZwqfeqHy4OIrY53ABcUlo8Ba6emnPyilAwDiJZG -57ILytgYNiro10Xlf+BUakX2+BVhz93UxZfOBouvYV+RqeQcMxr35K12r153sHdB -QFYN5rdk218CgYB8x4R1QXZAsOZ+o5YBZHb+pikm8VWQrfxoHB6SnWaZvBLMV+9P -YbP2GV7VgW+kNTHnubwQ3luqjGw600RgLZwGcIuVEsr2Do40BJp73Xm4zs3lec+K -XeNYUWSnO88elrjlJ5fE52n3dDkBeVEDGWGoPFTrp/RDWie3P0uV3C837QKBgQCi -H3m7lZ+uNuJyy+hhbc0Uy9KBzKZ7lKkKBuUqTlTaqTjZipsWE9QrzV8b+dBNlDks -k6JDBmJlbffxvCPTNvh5XQM3bT+6hGgynxaG9d596zKNZ44ua55/WOAjkU/ch5Nv -DVTfHHIVtmc+mMRfXYv1JpiS/UWa4ajHujEhbLcRXQKBgQDdOSGv3/JBk3rkOrKX -HdA3h7Ao2SiyaovOzKHbcG46IURpwrZFKQ7THFmeXellpOKX0qSiwKWb7o+m2pmZ -8G8op2k3a4vxZYhCzJKY2L3kFQ0EBbxrIdD77PWXf2EyABQfsu+bQ+JKdqUNagui -qLTyi7E5Lp0ZqsGMHe6IiSvWxg== ------END PRIVATE KEY----- \ No newline at end of file diff --git a/src/config/file.js b/src/config/file.js new file mode 100644 index 000000000..fd1f284d0 --- /dev/null +++ b/src/config/file.js @@ -0,0 +1,14 @@ +const path = require('path'); +// eslint-disable-next-line prefer-const +let configFile = undefined; + +module.exports = { + get configFile() { + return configFile + ? configFile + : path.join(process.cwd(), 'proxy.config.json'); + }, + set configFile(file) { + configFile = file; + }, +}; diff --git a/src/config/index.js b/src/config/index.js index 1435bd16a..e94f38c93 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,8 +1,7 @@ const fs = require('fs'); -const path = require('path'); -const defaultSettings = require('../../resources/config.json'); -const userSettingsPath = path.join(process.cwd(), './user-settings.json'); +const defaultSettings = require('../../proxy.config.json'); +const userSettingsPath = require('./file').configFile; let _userSettings = null; if (fs.existsSync(userSettingsPath)) { diff --git a/test/testConfig.js b/test/testConfig.js index 1a8091214..d0afb0618 100644 --- a/test/testConfig.js +++ b/test/testConfig.js @@ -2,7 +2,7 @@ const chai = require('chai'); const fs = require('fs'); const path = require('path'); -const defaultSettings = require('../resources/config.json'); +const defaultSettings = require('../proxy.config.json'); chai.should(); const expect = chai.expect; @@ -30,13 +30,11 @@ describe('default configuration', function () { describe('user configuration', function () { let tempDir; let tempUserFile; - let curDirUserFile; beforeEach(function () { tempDir = fs.mkdtempSync('gitproxy-test'); - tempUserFile = path.join(tempDir, 'user-settings.json'); - curDirUserFile = path.join(process.cwd(), 'user-settings.json'); - fs.symlinkSync(tempUserFile, curDirUserFile); + tempUserFile = path.join(tempDir, 'test-settings.json'); + require('../src/config/file').configFile = tempUserFile; }); it('should override default settings for authorisedList', function () { @@ -110,7 +108,6 @@ describe('user configuration', function () { }); afterEach(function () { - fs.unlinkSync(curDirUserFile); fs.rmSync(tempUserFile); fs.rmdirSync(tempDir); delete require.cache[require.resolve('../src/config')]; diff --git a/user-settings.json b/user-settings.json new file mode 100644 index 000000000..1d958b6c1 --- /dev/null +++ b/user-settings.json @@ -0,0 +1 @@ +{ "authorisedList": [ { "project": "foo", "name": "bar", "url": "https://github.com/foo/bar.git" } ] }