From 445145b827238997adc48bee28b8090ccaa5006b Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Fri, 24 Oct 2025 12:49:39 +0200 Subject: [PATCH 01/10] feat: add dependencies for SSH key management --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 26c39f64d..d09a9f7ef 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "dependencies": { "@material-ui/core": "^4.12.4", "@material-ui/icons": "4.11.3", + "@material-ui/lab": "^4.0.0-alpha.61", "@primer/octicons-react": "^19.18.0", "@seald-io/nedb": "^4.1.2", "axios": "^1.12.2", @@ -52,6 +53,7 @@ "concurrently": "^9.2.1", "connect-mongo": "^5.1.0", "cors": "^2.8.5", + "dayjs": "^1.11.13", "diff2html": "^3.4.52", "env-paths": "^3.0.0", "express": "^4.21.2", @@ -81,6 +83,7 @@ "react-router-dom": "6.30.1", "simple-git": "^3.28.0", "ssh2": "^1.16.0", + "sshpk": "^1.18.0", "uuid": "^11.1.0", "validator": "^13.15.15", "yargs": "^17.7.2" From d13bfaebaa573c6d211114314609118dfcb9d5b5 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Thu, 6 Nov 2025 15:48:40 +0100 Subject: [PATCH 02/10] feat(db): add PublicKeyRecord type for SSH key management --- src/db/types.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/db/types.ts b/src/db/types.ts index 6402f937c..d7f9de400 100644 --- a/src/db/types.ts +++ b/src/db/types.ts @@ -10,6 +10,13 @@ export type PushQuery = { export type UserRole = 'canPush' | 'canAuthorise'; +export type PublicKeyRecord = { + key: string; + name: string; + addedAt: string; + fingerprint: string; +}; + export class Repo { project: string; name: string; @@ -39,7 +46,7 @@ export class User { email: string; admin: boolean; oidcId?: string | null; - publicKeys?: string[]; + publicKeys?: PublicKeyRecord[]; _id?: string; constructor( @@ -49,7 +56,7 @@ export class User { email: string, admin: boolean, oidcId: string | null = null, - publicKeys: string[] = [], + publicKeys: PublicKeyRecord[] = [], _id?: string, ) { this.username = username; @@ -90,6 +97,7 @@ export interface Sink { createUser: (user: User) => Promise; deleteUser: (username: string) => Promise; updateUser: (user: User) => Promise; - addPublicKey: (username: string, publicKey: string) => Promise; - removePublicKey: (username: string, publicKey: string) => Promise; + addPublicKey: (username: string, publicKey: PublicKeyRecord) => Promise; + removePublicKey: (username: string, fingerprint: string) => Promise; + getPublicKeys: (username: string) => Promise; } From c6281421c5ccd894e143a384cd82a22ac713aae1 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Thu, 6 Nov 2025 15:48:47 +0100 Subject: [PATCH 03/10] feat(db): implement SSH key management for File database --- src/db/file/index.ts | 1 + src/db/file/users.ts | 41 +++++++++++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/db/file/index.ts b/src/db/file/index.ts index 68d8adc1a..e48bbfe03 100644 --- a/src/db/file/index.ts +++ b/src/db/file/index.ts @@ -28,4 +28,5 @@ export const { updateUser, addPublicKey, removePublicKey, + getPublicKeys, } = users; diff --git a/src/db/file/users.ts b/src/db/file/users.ts index 76742fb8f..15e4c7792 100644 --- a/src/db/file/users.ts +++ b/src/db/file/users.ts @@ -1,6 +1,6 @@ import fs from 'fs'; import Datastore from '@seald-io/nedb'; -import { User } from '../types'; +import { User, PublicKeyRecord } from '../types'; const COMPACTION_INTERVAL = 1000 * 60 * 60 * 24; // once per day @@ -176,7 +176,7 @@ export const getUsers = (query: any = {}): Promise => { }); }; -export const addPublicKey = (username: string, publicKey: string): Promise => { +export const addPublicKey = (username: string, publicKey: PublicKeyRecord): Promise => { return new Promise((resolve, reject) => { findUser(username) .then((user) => { @@ -187,20 +187,28 @@ export const addPublicKey = (username: string, publicKey: string): Promise if (!user.publicKeys) { user.publicKeys = []; } - if (!user.publicKeys.includes(publicKey)) { - user.publicKeys.push(publicKey); - updateUser(user) - .then(() => resolve()) - .catch(reject); - } else { - resolve(); + + // Check if key already exists (by key content or fingerprint) + const keyExists = user.publicKeys.some( + (k) => + k.key === publicKey.key || (k.fingerprint && k.fingerprint === publicKey.fingerprint), + ); + + if (keyExists) { + reject(new Error('SSH key already exists')); + return; } + + user.publicKeys.push(publicKey); + updateUser(user) + .then(() => resolve()) + .catch(reject); }) .catch(reject); }); }; -export const removePublicKey = (username: string, publicKey: string): Promise => { +export const removePublicKey = (username: string, fingerprint: string): Promise => { return new Promise((resolve, reject) => { findUser(username) .then((user) => { @@ -213,7 +221,7 @@ export const removePublicKey = (username: string, publicKey: string): Promise key !== publicKey); + user.publicKeys = user.publicKeys.filter((k) => k.fingerprint !== fingerprint); updateUser(user) .then(() => resolve()) .catch(reject); @@ -224,7 +232,7 @@ export const removePublicKey = (username: string, publicKey: string): Promise => { return new Promise((resolve, reject) => { - db.findOne({ publicKeys: sshKey }, (err: Error | null, doc: User) => { + db.findOne({ 'publicKeys.key': sshKey }, (err: Error | null, doc: User) => { // ignore for code coverage as neDB rarely returns errors even for an invalid query /* istanbul ignore if */ if (err) { @@ -239,3 +247,12 @@ export const findUserBySSHKey = (sshKey: string): Promise => { }); }); }; + +export const getPublicKeys = (username: string): Promise => { + return findUser(username).then((user) => { + if (!user) { + throw new Error('User not found'); + } + return user.publicKeys || []; + }); +}; From 0ab955aac612617643d671b687db1ba2fcafc202 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Thu, 6 Nov 2025 15:48:54 +0100 Subject: [PATCH 04/10] feat(db): implement SSH key management for MongoDB --- src/db/mongo/index.ts | 1 + src/db/mongo/users.ts | 35 +++++++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/db/mongo/index.ts b/src/db/mongo/index.ts index 78c7dfce0..a793effa1 100644 --- a/src/db/mongo/index.ts +++ b/src/db/mongo/index.ts @@ -31,4 +31,5 @@ export const { updateUser, addPublicKey, removePublicKey, + getPublicKeys, } = users; diff --git a/src/db/mongo/users.ts b/src/db/mongo/users.ts index 74a102cc2..21b4cf6a2 100644 --- a/src/db/mongo/users.ts +++ b/src/db/mongo/users.ts @@ -1,6 +1,6 @@ import { OptionalId, Document, ObjectId } from 'mongodb'; import { toClass } from '../helper'; -import { User } from '../types'; +import { User, PublicKeyRecord } from '../types'; import { connect } from './helper'; import _ from 'lodash'; const collectionName = 'users'; @@ -68,24 +68,47 @@ export const updateUser = async (user: User): Promise => { await collection.updateOne(filter, { $set: userWithoutId }, options); }; -export const addPublicKey = async (username: string, publicKey: string): Promise => { +export const addPublicKey = async (username: string, publicKey: PublicKeyRecord): Promise => { const collection = await connect(collectionName); + + const user = await collection.findOne({ username: username.toLowerCase() }); + if (!user) { + throw new Error('User not found'); + } + + const keyExists = user.publicKeys?.some( + (k: PublicKeyRecord) => + k.key === publicKey.key || (k.fingerprint && k.fingerprint === publicKey.fingerprint), + ); + + if (keyExists) { + throw new Error('SSH key already exists'); + } + await collection.updateOne( { username: username.toLowerCase() }, - { $addToSet: { publicKeys: publicKey } }, + { $push: { publicKeys: publicKey } }, ); }; -export const removePublicKey = async (username: string, publicKey: string): Promise => { +export const removePublicKey = async (username: string, fingerprint: string): Promise => { const collection = await connect(collectionName); await collection.updateOne( { username: username.toLowerCase() }, - { $pull: { publicKeys: publicKey } }, + { $pull: { publicKeys: { fingerprint: fingerprint } } }, ); }; export const findUserBySSHKey = async function (sshKey: string): Promise { const collection = await connect(collectionName); - const doc = await collection.findOne({ publicKeys: { $eq: sshKey } }); + const doc = await collection.findOne({ 'publicKeys.key': { $eq: sshKey } }); return doc ? toClass(doc, User.prototype) : null; }; + +export const getPublicKeys = async (username: string): Promise => { + const user = await findUser(username); + if (!user) { + throw new Error('User not found'); + } + return user.publicKeys || []; +}; From 4d6ebd9497d895ebd76137b6f93e7efdd5bf4f75 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Thu, 6 Nov 2025 15:49:01 +0100 Subject: [PATCH 05/10] feat(db): update database wrapper with correct SSH key types --- src/db/index.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/db/index.ts b/src/db/index.ts index 71cc50f92..09c38b0c8 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,5 +1,5 @@ import { AuthorisedRepo } from '../config/types'; -import { PushQuery, Repo, Sink, User } from './types'; +import { PushQuery, Repo, Sink, User, PublicKeyRecord } from './types'; import * as bcrypt from 'bcryptjs'; import * as config from '../config'; import * as mongo from './mongo'; @@ -172,3 +172,9 @@ export const findUserBySSHKey = (sshKey: string): Promise => export const getUsers = (query?: object): Promise => sink.getUsers(query); export const deleteUser = (username: string): Promise => sink.deleteUser(username); export const updateUser = (user: User): Promise => sink.updateUser(user); +export const addPublicKey = (username: string, publicKey: PublicKeyRecord): Promise => + sink.addPublicKey(username, publicKey); +export const removePublicKey = (username: string, fingerprint: string): Promise => + sink.removePublicKey(username, fingerprint); +export const getPublicKeys = (username: string): Promise => + sink.getPublicKeys(username); From 70b8cdb56d2e6b05fa9e019ff44c7e38a98da804 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Thu, 6 Nov 2025 15:52:03 +0100 Subject: [PATCH 06/10] feat(api): add SSH key management endpoints --- src/service/routes/config.js | 4 ++ src/service/routes/users.js | 103 ++++++++++++++++++++++++++++++----- 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/src/service/routes/config.js b/src/service/routes/config.js index e80d70b5b..054ffb0c9 100644 --- a/src/service/routes/config.js +++ b/src/service/routes/config.js @@ -19,4 +19,8 @@ router.get('/uiRouteAuth', function ({ res }) { res.send(config.getUIRouteAuth()); }); +router.get('/ssh', function ({ res }) { + res.send(config.getSSHConfig()); +}); + module.exports = router; diff --git a/src/service/routes/users.js b/src/service/routes/users.js index d378760e5..7690b14b2 100644 --- a/src/service/routes/users.js +++ b/src/service/routes/users.js @@ -2,6 +2,25 @@ const express = require('express'); const router = new express.Router(); const db = require('../../db'); const { toPublicUser } = require('./publicApi'); +const { utils } = require('ssh2'); +const crypto = require('crypto'); + +// Calculate SHA-256 fingerprint from SSH public key +// Note: This function is duplicated in src/cli/ssh-key.ts to keep CLI and server independent +function calculateFingerprint(publicKeyStr) { + try { + const parsed = utils.parseKey(publicKeyStr); + if (!parsed || parsed instanceof Error) { + return null; + } + const pubKey = parsed.getPublicSSH(); + const hash = crypto.createHash('sha256').update(pubKey).digest('base64'); + return `SHA256:${hash}`; + } catch (err) { + console.error('Error calculating fingerprint:', err); + return null; + } +} router.get('/', async (req, res) => { console.log(`fetching users`); @@ -16,6 +35,35 @@ router.get('/:id', async (req, res) => { res.send(toPublicUser(user)); }); +// Get SSH key fingerprints for a user +router.get('/:username/ssh-key-fingerprints', async (req, res) => { + if (!req.user) { + res.status(401).json({ error: 'Authentication required' }); + return; + } + + const targetUsername = req.params.username.toLowerCase(); + + // Only allow users to view their own keys, or admins to view any keys + if (req.user.username !== targetUsername && !req.user.admin) { + res.status(403).json({ error: 'Not authorized to view keys for this user' }); + return; + } + + try { + const publicKeys = await db.getPublicKeys(targetUsername); + const keyFingerprints = publicKeys.map((keyRecord) => ({ + fingerprint: keyRecord.fingerprint, + name: keyRecord.name, + addedAt: keyRecord.addedAt, + })); + res.json(keyFingerprints); + } catch (error) { + console.error('Error retrieving SSH keys:', error); + res.status(500).json({ error: 'Failed to retrieve SSH keys' }); + } +}); + // Add SSH public key router.post('/:username/ssh-keys', async (req, res) => { if (!req.user) { @@ -31,33 +79,59 @@ router.post('/:username/ssh-keys', async (req, res) => { return; } - const { publicKey } = req.body; + const { publicKey, name } = req.body; if (!publicKey) { res.status(400).json({ error: 'Public key is required' }); return; } // Strip the comment from the key (everything after the last space) - const keyWithoutComment = publicKey.split(' ').slice(0, 2).join(' '); + const keyWithoutComment = publicKey.trim().split(' ').slice(0, 2).join(' '); - console.log('Adding SSH key', { targetUsername, keyWithoutComment }); + // Calculate fingerprint + const fingerprint = calculateFingerprint(keyWithoutComment); + if (!fingerprint) { + res.status(400).json({ error: 'Invalid SSH public key format' }); + return; + } + + const publicKeyRecord = { + key: keyWithoutComment, + name: name || 'Unnamed Key', + addedAt: new Date().toISOString(), + fingerprint: fingerprint, + }; + + console.log('Adding SSH key', { targetUsername, fingerprint }); try { - await db.addPublicKey(targetUsername, keyWithoutComment); - res.status(201).json({ message: 'SSH key added successfully' }); + await db.addPublicKey(targetUsername, publicKeyRecord); + res.status(201).json({ + message: 'SSH key added successfully', + fingerprint: fingerprint, + }); } catch (error) { console.error('Error adding SSH key:', error); - res.status(500).json({ error: 'Failed to add SSH key' }); + + // Return specific error message + if (error.message === 'SSH key already exists') { + res.status(409).json({ error: 'This SSH key already exists' }); + } else if (error.message === 'User not found') { + res.status(404).json({ error: 'User not found' }); + } else { + res.status(500).json({ error: error.message || 'Failed to add SSH key' }); + } } }); -// Remove SSH public key -router.delete('/:username/ssh-keys', async (req, res) => { +// Remove SSH public key by fingerprint +router.delete('/:username/ssh-keys/:fingerprint', async (req, res) => { if (!req.user) { res.status(401).json({ error: 'Authentication required' }); return; } const targetUsername = req.params.username.toLowerCase(); + const fingerprint = req.params.fingerprint; // Only allow users to remove keys from their own account, or admins to remove from any account if (req.user.username !== targetUsername && !req.user.admin) { @@ -65,18 +139,21 @@ router.delete('/:username/ssh-keys', async (req, res) => { return; } - const { publicKey } = req.body; - if (!publicKey) { - res.status(400).json({ error: 'Public key is required' }); + if (!fingerprint) { + res.status(400).json({ error: 'Fingerprint is required' }); return; } try { - await db.removePublicKey(targetUsername, publicKey); + await db.removePublicKey(targetUsername, fingerprint); res.status(200).json({ message: 'SSH key removed successfully' }); } catch (error) { console.error('Error removing SSH key:', error); - res.status(500).json({ error: 'Failed to remove SSH key' }); + if (error.message === 'User not found') { + res.status(404).json({ error: 'User not found' }); + } else { + res.status(500).json({ error: 'Failed to remove SSH key' }); + } } }); From 86eabba1545c2a2610699feef815a718ab88bede Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Thu, 6 Nov 2025 15:52:10 +0100 Subject: [PATCH 07/10] feat(ui): add SSH service for API calls --- src/ui/services/ssh.ts | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/ui/services/ssh.ts diff --git a/src/ui/services/ssh.ts b/src/ui/services/ssh.ts new file mode 100644 index 000000000..fb5d1e9dc --- /dev/null +++ b/src/ui/services/ssh.ts @@ -0,0 +1,51 @@ +import axios, { AxiosResponse } from 'axios'; +import { getAxiosConfig } from './auth'; +import { API_BASE } from '../apiBase'; + +export interface SSHKey { + fingerprint: string; + name: string; + addedAt: string; +} + +export interface SSHConfig { + enabled: boolean; + port: number; + host?: string; +} + +export const getSSHConfig = async (): Promise => { + const response: AxiosResponse = await axios( + `${API_BASE}/api/v1/config/ssh`, + getAxiosConfig(), + ); + return response.data; +}; + +export const getSSHKeys = async (username: string): Promise => { + const response: AxiosResponse = await axios( + `${API_BASE}/api/v1/user/${username}/ssh-key-fingerprints`, + getAxiosConfig(), + ); + return response.data; +}; + +export const addSSHKey = async ( + username: string, + publicKey: string, + name: string, +): Promise<{ message: string; fingerprint: string }> => { + const response: AxiosResponse<{ message: string; fingerprint: string }> = await axios.post( + `${API_BASE}/api/v1/user/${username}/ssh-keys`, + { publicKey, name }, + getAxiosConfig(), + ); + return response.data; +}; + +export const deleteSSHKey = async (username: string, fingerprint: string): Promise => { + await axios.delete( + `${API_BASE}/api/v1/user/${username}/ssh-keys/${encodeURIComponent(fingerprint)}`, + getAxiosConfig(), + ); +}; From 7901e3a456764880d7451e1ced4170aa8caee272 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Thu, 6 Nov 2025 15:52:16 +0100 Subject: [PATCH 08/10] feat(ui): add SSH key management UI and clone tabs --- .../CustomButtons/CodeActionButton.tsx | 59 ++- src/ui/views/User/User.tsx | 375 ++++++++++++++---- 2 files changed, 347 insertions(+), 87 deletions(-) diff --git a/src/ui/components/CustomButtons/CodeActionButton.tsx b/src/ui/components/CustomButtons/CodeActionButton.tsx index 5fb9d6588..ffc556c5b 100644 --- a/src/ui/components/CustomButtons/CodeActionButton.tsx +++ b/src/ui/components/CustomButtons/CodeActionButton.tsx @@ -8,9 +8,11 @@ import { CopyIcon, TerminalIcon, } from '@primer/octicons-react'; -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { PopperPlacementType } from '@material-ui/core/Popper'; import Button from './Button'; +import { Tabs, Tab } from '@material-ui/core'; +import { getSSHConfig, SSHConfig } from '../../services/ssh'; interface CodeActionButtonProps { cloneURL: string; @@ -21,6 +23,32 @@ const CodeActionButton: React.FC = ({ cloneURL }) => { const [open, setOpen] = useState(false); const [placement, setPlacement] = useState(); const [isCopied, setIsCopied] = useState(false); + const [selectedTab, setSelectedTab] = useState(0); + const [sshConfig, setSshConfig] = useState(null); + const [sshURL, setSSHURL] = useState(''); + + // Load SSH config on mount + useEffect(() => { + const loadSSHConfig = async () => { + try { + const config = await getSSHConfig(); + setSshConfig(config); + + // Calculate SSH URL from HTTPS URL + if (config.enabled && cloneURL) { + // Convert https://proxy-host/github.com/user/repo.git to git@proxy-host:github.com/user/repo.git + const url = new URL(cloneURL); + const host = url.host; + const path = url.pathname.substring(1); // remove leading / + const port = config.port !== 22 ? `:${config.port}` : ''; + setSSHURL(`git@${host}${port}:${path}`); + } + } catch (error) { + console.error('Error loading SSH config:', error); + } + }; + loadSSHConfig(); + }, [cloneURL]); const handleClick = (newPlacement: PopperPlacementType) => (event: React.MouseEvent) => { @@ -34,6 +62,14 @@ const CodeActionButton: React.FC = ({ cloneURL }) => { setOpen(false); }; + const handleTabChange = (_event: React.ChangeEvent, newValue: number) => { + setSelectedTab(newValue); + setIsCopied(false); + }; + + const currentURL = selectedTab === 0 ? cloneURL : sshURL; + const currentCloneCommand = selectedTab === 0 ? `git clone ${cloneURL}` : `git clone ${sshURL}`; + return ( <> + + + - - ) : null} - - - - - + ) : null} + + + + + setSnackbarOpen(false)} + close + /> + + + {/* SSH Key Modal */} + + + Add New SSH Key + + + + + + + + + + + ); } From d29bf444cdb080836ea04a964fc880e4933c8e4f Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Thu, 6 Nov 2025 15:52:23 +0100 Subject: [PATCH 09/10] feat(cli): update SSH key deletion to use fingerprint --- src/cli/ssh-key.ts | 48 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/src/cli/ssh-key.ts b/src/cli/ssh-key.ts index de1182a77..602300c67 100644 --- a/src/cli/ssh-key.ts +++ b/src/cli/ssh-key.ts @@ -3,6 +3,8 @@ import * as fs from 'fs'; import * as path from 'path'; import axios from 'axios'; +import { utils } from 'ssh2'; +import * as crypto from 'crypto'; const API_BASE_URL = process.env.GIT_PROXY_API_URL || 'http://localhost:3000'; const GIT_PROXY_COOKIE_FILE = path.join( @@ -23,6 +25,23 @@ interface ErrorWithResponse { message: string; } +// Calculate SHA-256 fingerprint from SSH public key +// Note: This function is duplicated in src/service/routes/users.js to keep CLI and server independent +function calculateFingerprint(publicKeyStr: string): string | null { + try { + const parsed = utils.parseKey(publicKeyStr); + if (!parsed || parsed instanceof Error) { + return null; + } + const pubKey = parsed.getPublicSSH(); + const hash = crypto.createHash('sha256').update(pubKey).digest('base64'); + return `SHA256:${hash}`; + } catch (err) { + console.error('Error calculating fingerprint:', err); + return null; + } +} + async function addSSHKey(username: string, keyPath: string): Promise { try { // Check for authentication @@ -90,15 +109,28 @@ async function removeSSHKey(username: string, keyPath: string): Promise { // Read the public key file const publicKey = fs.readFileSync(keyPath, 'utf8').trim(); - // Make the API request - await axios.delete(`${API_BASE_URL}/api/v1/user/${username}/ssh-keys`, { - data: { publicKey }, - withCredentials: true, - headers: { - 'Content-Type': 'application/json', - Cookie: cookies, + // Strip the comment from the key (everything after the last space) + const keyWithoutComment = publicKey.split(' ').slice(0, 2).join(' '); + + // Calculate fingerprint + const fingerprint = calculateFingerprint(keyWithoutComment); + if (!fingerprint) { + console.error('Invalid SSH key format. Unable to calculate fingerprint.'); + process.exit(1); + } + + console.log(`Removing SSH key with fingerprint: ${fingerprint}`); + + // Make the API request using fingerprint in path + await axios.delete( + `${API_BASE_URL}/api/v1/user/${username}/ssh-keys/${encodeURIComponent(fingerprint)}`, + { + withCredentials: true, + headers: { + Cookie: cookies, + }, }, - }); + ); console.log('SSH key removed successfully!'); } catch (error) { From 053d39e567e841724ddd3613b248b63225e70174 Mon Sep 17 00:00:00 2001 From: fabiovincenzi Date: Thu, 6 Nov 2025 16:14:02 +0100 Subject: [PATCH 10/10] chore: regenerate config types after schema changes --- src/config/generated/config.ts | 50 ---------------------------------- 1 file changed, 50 deletions(-) diff --git a/src/config/generated/config.ts b/src/config/generated/config.ts index 147d29e4c..ba3c2fbb0 100644 --- a/src/config/generated/config.ts +++ b/src/config/generated/config.ts @@ -53,10 +53,6 @@ export interface GitProxyConfig { * Provide domains to use alternative to the defaults */ domains?: { [key: string]: any }; - /** - * Limits for git operations such as maximum pack size - */ - limits?: Limits; /** * List of plugins to integrate on GitProxy's push or pull actions. Each value is either a * file path or a module name. @@ -152,17 +148,6 @@ export interface Gitleaks { [property: string]: any; } -/** - * Limits for git operations - */ -export interface Limits { - /** - * Maximum allowed size of git packfiles in bytes - */ - maxPackSizeBytes?: number; - [property: string]: any; -} - /** * Configuration used in conjunction with ActiveDirectory auth, which relates to a REST API * used to check user group membership, as opposed to direct querying via LDAP.
If this @@ -330,10 +315,6 @@ export interface SSH { * Port for SSH proxy server to listen on */ port?: number; - /** - * Credentials used when cloning repositories for SSH-originated pushes - */ - clone?: SSHClone; [property: string]: any; } @@ -352,23 +333,6 @@ export interface HostKey { [property: string]: any; } -/** - * Configuration for cloning repositories during SSH pushes - */ -export interface SSHClone { - serviceToken?: ServiceToken; - [property: string]: any; -} - -/** - * Basic authentication credentials used for cloning operations - */ -export interface ServiceToken { - username?: string; - password?: string; - [property: string]: any; -} - /** * Toggle the generation of temporary password for git-proxy admin user */ @@ -610,7 +574,6 @@ const typeMap: any = { { json: 'cookieSecret', js: 'cookieSecret', typ: u(undefined, '') }, { json: 'csrfProtection', js: 'csrfProtection', typ: u(undefined, true) }, { json: 'domains', js: 'domains', typ: u(undefined, m('any')) }, - { json: 'limits', js: 'limits', typ: u(undefined, r('Limits')) }, { json: 'plugins', js: 'plugins', typ: u(undefined, a('')) }, { json: 'privateOrganizations', js: 'privateOrganizations', typ: u(undefined, a('any')) }, { json: 'proxyUrl', js: 'proxyUrl', typ: u(undefined, '') }, @@ -645,7 +608,6 @@ const typeMap: any = { ], 'any', ), - Limits: o([{ json: 'maxPackSizeBytes', js: 'maxPackSizeBytes', typ: u(undefined, 3.14) }], 'any'), Ls: o([{ json: 'userInADGroup', js: 'userInADGroup', typ: u(undefined, '') }], false), AuthenticationElement: o( [ @@ -718,7 +680,6 @@ const typeMap: any = { { json: 'enabled', js: 'enabled', typ: true }, { json: 'hostKey', js: 'hostKey', typ: u(undefined, r('HostKey')) }, { json: 'port', js: 'port', typ: u(undefined, 3.14) }, - { json: 'clone', js: 'clone', typ: u(undefined, r('SSHClone')) }, ], 'any', ), @@ -729,17 +690,6 @@ const typeMap: any = { ], 'any', ), - SSHClone: o( - [{ json: 'serviceToken', js: 'serviceToken', typ: u(undefined, r('ServiceToken')) }], - 'any', - ), - ServiceToken: o( - [ - { json: 'username', js: 'username', typ: u(undefined, '') }, - { json: 'password', js: 'password', typ: u(undefined, '') }, - ], - 'any', - ), TempPassword: o( [ { json: 'emailConfig', js: 'emailConfig', typ: u(undefined, m('any')) },