You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
4.6 KiB
150 lines
4.6 KiB
"use strict"; |
|
|
|
const Promise = require("bluebird"); |
|
const bhttp = require("bhttp"); |
|
const bodyParser = require("body-parser"); |
|
const cookieParser = require("cookie-parser"); |
|
const debug = require("debug")("gitea-registration-proxy"); |
|
const express = require("express"); |
|
const Router = require("express-promise-router"); |
|
const toughCookie = require("tough-cookie"); |
|
const JSDOM = require("jsdom").JSDOM; |
|
|
|
|
|
const setting = { |
|
gitea: "127.0.0.1:3000", |
|
port: "8080", |
|
host: "0.0.0.0", |
|
inviteCode: "change_meeee", |
|
}; |
|
|
|
Object.keys(setting).forEach(k => { |
|
const valueFromEnv = process.env[`REGPROXY_${k.toUpperCase()}`]; |
|
if(valueFromEnv !== undefined) { |
|
setting[k] = valueFromEnv; |
|
} |
|
}) |
|
|
|
const xssSanitize = (input) => { |
|
const inputString = String(input || ""); |
|
console.log(inputString); |
|
return inputString.replace(/[^a-zA-Z0-9_]+/g, "_").replace(/(^_)|(_$)/g, ""); |
|
}; |
|
|
|
if(setting.inviteCode == "") { |
|
console.error(`inviteCode '${setting.inviteCode}' must not be left empty`); |
|
process.exit(1); |
|
} |
|
|
|
if(setting.inviteCode == "change_meeee") { |
|
console.error(`inviteCode '${setting.inviteCode}' must not be left as its default value`); |
|
process.exit(1); |
|
} |
|
|
|
if(xssSanitize(setting.inviteCode) != setting.inviteCode) { |
|
console.error(`inviteCode '${setting.inviteCode}' must only contain alphanumeric characters and underscores`); |
|
process.exit(1); |
|
} |
|
|
|
setting.version = require(__dirname + "/package.json").version; |
|
debug("Starting gitea-registration-proxy %s", setting.version); |
|
|
|
|
|
const app = express(); |
|
|
|
const router = Router(); |
|
|
|
app.use(router); |
|
|
|
/* |
|
POST /user/sign_up |
|
Form data: |
|
_csrf |
|
user_name |
|
email |
|
password |
|
retype |
|
challenge |
|
nonce |
|
*/ |
|
|
|
const getBhttpSessionFromRequest = req => { |
|
let jar = new toughCookie.CookieJar(); |
|
let passedCookies = "i_like_gitea, session, lang, _csrf".split(", "); |
|
passedCookies.forEach((cookieKey) => { |
|
if (req.cookies && req.cookies[cookieKey] != undefined) { |
|
jar.setCookie(new toughCookie.Cookie({key: cookieKey, value: req.cookies[cookieKey], secure: false}), setting.gitea); |
|
} |
|
}); |
|
|
|
return bhttp.session({cookieJar: jar}); |
|
}; |
|
|
|
|
|
const returnFormResponse = (upstreamResponse, res, inviteCode) => { |
|
|
|
const dom = new JSDOM(upstreamResponse.body); |
|
|
|
const fieldContainer = dom.window.document.createElement("div"); |
|
fieldContainer.className = "required inline field"; |
|
fieldContainer.style = "display: flex; align-items: center;" |
|
const label = dom.window.document.createElement("label"); |
|
label.for = "invite_code"; |
|
label.textContent = "Invite Code"; |
|
fieldContainer.appendChild(label); |
|
|
|
const inviteCodeInput = dom.window.document.createElement("input"); |
|
inviteCodeInput.type = "password"; |
|
inviteCodeInput.name = "invite_code"; |
|
|
|
// prevent xss reflection: since the user can type anything into this, we enforce strict rules on how its interpreted. |
|
// the app wont even start if the correct invite code doesn't follow this format, so its should be fine to do this here. |
|
inviteCodeInput.value = xssSanitize(inviteCode); |
|
|
|
fieldContainer.appendChild(inviteCodeInput); |
|
|
|
const allRequiredFields = dom.window.document.querySelectorAll('form .required.field'); |
|
const lastRequiredField = allRequiredFields[allRequiredFields.length-1]; |
|
if(lastRequiredField) { |
|
lastRequiredField.insertAdjacentElement('afterend', fieldContainer); |
|
} |
|
|
|
res.send(dom.serialize()); |
|
}; |
|
|
|
router.get("/user/sign_up", (req, res) => { |
|
return Promise.try(() => getBhttpSessionFromRequest(req).get(`${setting.gitea}/user/sign_up`)) |
|
.then((upstreamResponse) => { |
|
returnFormResponse(upstreamResponse, res); |
|
}); |
|
}); |
|
|
|
router.post("/user/sign_up", bodyParser.urlencoded({ extended: false }), cookieParser(), (req, res) => { |
|
const form = req.body; |
|
debug(`Signup request: %s (%s)`, form.user_name, form.email, form.challenge, form.nonce); |
|
|
|
if(form.invite_code == setting.inviteCode) { |
|
debug("✅ valid invite_code, forwarding request"); |
|
|
|
return Promise.try(() => { |
|
let passedForm = "_csrf, user_name, email, password, retype".split(", "); |
|
let newForm = {}; |
|
passedForm.forEach((field) => { |
|
if (form[field] != undefined) { |
|
newForm[field] = form[field]; |
|
} |
|
}); |
|
return getBhttpSessionFromRequest(req).post(`${setting.gitea}/user/sign_up`, newForm) |
|
.then((upstreamResponse) => { |
|
returnFormResponse(upstreamResponse, res, form.invite_code); |
|
}); |
|
}); |
|
|
|
} else { |
|
debug("401 unauthorized, invalid invite code: " + setting.inviteCode); |
|
res.write("401 unauthorized: invalid invite code\n"); |
|
return res.end(); |
|
} |
|
}); |
|
|
|
app.listen(Number(setting.port), setting.host); |