routes/smart-http.js - git - Webernet Explorer _X
git 🏠 Home 👤 Sign up Self hosted git, retro shell
★ otgit, code hosting from the year 2000 ★
📄 octuna / routes / smart-http.js
87 lines · 3.0 KB · Raw
1const auth = require('../lib/auth');
2const data = require('../lib/data');
3const backend = require('../lib/http-backend');
4const respond = require('../lib/respond');
5const validate = require('../lib/validate');
6const limit = require('../lib/limit');
7const config = require('../lib/config');
8 
9const pushLimiter = limit.make(
10 config.get('limits.push.max'),
11 config.get('limits.push.windowMin') * 60000
12);
13 
14function parseBasicAuth(req) {
15 const h = req.headers['authorization'];
16 if (!h || !h.startsWith('Basic ')) return null;
17 try {
18 const decoded = Buffer.from(h.slice(6), 'base64').toString('utf8');
19 const sep = decoded.indexOf(':');
20 if (sep < 0) return null;
21 return { username: decoded.slice(0, sep).toLowerCase(), password: decoded.slice(sep + 1) };
22 } catch { return null; }
23}
24 
25function require401(res) {
26 res.writeHead(401, {
27 'WWW-Authenticate': `Basic realm="${config.get('site.name')}"`,
28 'Content-Type': 'text/plain',
29 ...respond.securityHeaders()
30 });
31 res.end('Authentication required');
32}
33 
34function checkAccess(req, res, owner, repo, isWrite) {
35 if (!validate.isValidName(owner) || !validate.isValidName(repo)) {
36 respond.notFound(res); return false;
37 }
38 const repoRecord = data.repos().get(`${owner}/${repo}`);
39 if (!repoRecord) { respond.notFound(res); return false; }
40 
41 const creds = parseBasicAuth(req);
42 
43 if (isWrite) {
44 if (!creds) { require401(res); return false; }
45 const ip = respond.clientIp(req);
46 if (!pushLimiter(ip)) { respond.tooMany(res); return false; }
47 const user = data.users().get(creds.username);
48 if (!user || !auth.verifyPassword(creds.password, user) || creds.username !== owner) {
49 require401(res); return false;
50 }
51 return true;
52 }
53 
54 if (repoRecord.visibility === 'public') return true;
55 
56 if (!creds) { require401(res); return false; }
57 const user = data.users().get(creds.username);
58 if (!user || !auth.verifyPassword(creds.password, user) || creds.username !== owner) {
59 require401(res); return false;
60 }
61 return true;
62}
63 
64function infoRefs(req, res, params) {
65 const qs = req.url.includes('?') ? req.url.slice(req.url.indexOf('?') + 1) : '';
66 const service = new URLSearchParams(qs).get('service') || '';
67 const isWrite = service === 'git-receive-pack';
68 if (!checkAccess(req, res, params.owner, params.repo, isWrite)) return;
69 backend.serve(req, res, params.owner, params.repo, 'info/refs');
70}
71 
72function uploadPack(req, res, params) {
73 if (!checkAccess(req, res, params.owner, params.repo, false)) return;
74 backend.serve(req, res, params.owner, params.repo, 'git-upload-pack');
75}
76 
77function receivePack(req, res, params) {
78 if (!checkAccess(req, res, params.owner, params.repo, true)) return;
79 backend.serve(req, res, params.owner, params.repo, 'git-receive-pack');
80}
81 
82module.exports = function register(router) {
83 router.get('/:owner/:repo.git/info/refs', infoRefs);
84 router.post('/:owner/:repo.git/git-upload-pack', uploadPack);
85 router.post('/:owner/:repo.git/git-receive-pack', receivePack);
86};
87 
Done 🔒 Internet