77import java .net .URISyntaxException ;
88import java .nio .file .InvalidPathException ;
99import java .nio .file .Path ;
10+ import java .util .Collections ;
11+ import java .util .List ;
12+ import java .util .Map ;
13+ import java .util .Optional ;
1014
1115import jakarta .annotation .Nullable ;
1216import jakarta .annotation .PostConstruct ;
1721import org .eclipse .jgit .api .CloneCommand ;
1822import org .eclipse .jgit .api .Git ;
1923import org .eclipse .jgit .api .GitCommand ;
24+ import org .eclipse .jgit .api .LsRemoteCommand ;
2025import org .eclipse .jgit .api .TransportCommand ;
26+ import org .eclipse .jgit .api .TransportConfigCallback ;
2127import org .eclipse .jgit .api .errors .GitAPIException ;
2228import org .eclipse .jgit .api .errors .InvalidRefNameException ;
29+ import org .eclipse .jgit .errors .UnsupportedCredentialItem ;
30+ import org .eclipse .jgit .transport .CredentialItem ;
2331import org .eclipse .jgit .transport .CredentialsProvider ;
32+ import org .eclipse .jgit .transport .SshConfigStore ;
33+ import org .eclipse .jgit .transport .SshConstants ;
34+ import org .eclipse .jgit .transport .SshTransport ;
35+ import org .eclipse .jgit .transport .URIish ;
2436import org .eclipse .jgit .transport .UsernamePasswordCredentialsProvider ;
37+ import org .eclipse .jgit .transport .sshd .JGitKeyCache ;
38+ import org .eclipse .jgit .transport .sshd .SshdSessionFactory ;
39+ import org .eclipse .jgit .transport .sshd .SshdSessionFactoryBuilder ;
2540import org .slf4j .Logger ;
2641import org .slf4j .LoggerFactory ;
2742import org .springframework .beans .factory .annotation .Value ;
@@ -50,21 +65,19 @@ public class BuildJobGitService extends AbstractGitService {
5065 @ Value ("${artemis.version-control.build-agent-use-ssh:false}" )
5166 private boolean useSshForBuildAgent ;
5267
68+ @ Value ("${artemis.version-control.ssh-private-key-folder-path:#{null}}" )
69+ private Optional <String > gitSshPrivateKeyPath ;
70+
71+ @ Value ("${artemis.version-control.ssh-template-clone-url:#{null}}" )
72+ private Optional <String > sshUrlTemplate ;
73+
5374 private CredentialsProvider credentialsProvider ;
5475
55- /**
56- * Determines whether to use SSH.
57- * <p>
58- * This method overrides the default behavior of {@code AbstractGitService} and returns the configuration flag
59- * {@code useSshForBuildAgent} to indicate if SSH should be used.
60- * </p>
61- *
62- * @return {@code true} if SSH should be used; {@code false} otherwise.
63- */
64- @ Override
65- protected boolean useSsh () {
66- return useSshForBuildAgent ;
67- }
76+ private JGitKeyCache jgitKeyCache ;
77+
78+ protected TransportConfigCallback sshCallback ;
79+
80+ private SshdSessionFactory sshdSessionFactory ;
6881
6982 /**
7083 * initialize the BuildJobGitService, in particular which authentication mechanism should be used
@@ -89,10 +102,108 @@ public void init() {
89102 }
90103 }
91104
105+ protected boolean useSsh () {
106+ return useSshForBuildAgent ;
107+ }
108+
109+ /**
110+ * Configures the SSH settings for the JGit SSH session factory.
111+ */
112+ protected void configureSsh () {
113+ CredentialsProvider .setDefault (new CustomCredentialsProvider ());
114+ final var sshSessionFactoryBuilder = getSshdSessionFactoryBuilder (gitSshPrivateKeyPath , localVCBaseUri );
115+ jgitKeyCache = new JGitKeyCache ();
116+ sshdSessionFactory = sshSessionFactoryBuilder .build (jgitKeyCache );
117+ sshCallback = transport -> {
118+ if (transport instanceof SshTransport sshTransport ) {
119+ transport .setTimeout (JGIT_TIMEOUT_IN_SECONDS );
120+ sshTransport .setSshSessionFactory (sshdSessionFactory );
121+ }
122+ else {
123+ log .error ("Cannot use ssh properly because of mismatch of Jgit transport object: {}" , transport );
124+ }
125+ };
126+ }
127+
128+ protected static SshdSessionFactoryBuilder getSshdSessionFactoryBuilder (Optional <String > gitSshPrivateKeyPath , URI gitUri ) {
129+ // @formatter:off
130+ return new SshdSessionFactoryBuilder ()
131+ .setConfigStoreFactory ((homeDir , configFile , localUserName ) -> new CustomSshConfigStore (gitUri ))
132+ .setSshDirectory (Path .of (gitSshPrivateKeyPath .orElseThrow ()).toFile ())
133+ .setHomeDirectory (Path .of (System .getProperty ("user.home" )).toFile ());
134+ // @formatter:on
135+ }
136+
92137 @ PreDestroy
93- @ Override
94138 public void cleanup () {
95- super .cleanup ();
139+ if (useSsh ()) {
140+ jgitKeyCache .close ();
141+ sshdSessionFactory .close ();
142+ }
143+ }
144+
145+ static class CustomCredentialsProvider extends CredentialsProvider {
146+
147+ @ Override
148+ public boolean isInteractive () {
149+ return false ;
150+ }
151+
152+ @ Override
153+ public boolean supports (CredentialItem ... items ) {
154+ return true ;
155+ }
156+
157+ // Note: the following method allows us to store known hosts
158+ @ Override
159+ public boolean get (URIish uri , CredentialItem ... items ) throws UnsupportedCredentialItem {
160+ for (CredentialItem item : items ) {
161+ if (item instanceof CredentialItem .YesNoType yesNoItem ) {
162+ yesNoItem .setValue (true );
163+ }
164+ }
165+ return true ;
166+ }
167+ }
168+
169+ record CustomSshConfigStore (URI gitUri ) implements SshConfigStore {
170+
171+ @ Override
172+ public HostConfig lookup (String hostName , int port , String userName ) {
173+ return new HostConfig () {
174+
175+ @ Override
176+ public String getValue (String key ) {
177+ return null ;
178+ }
179+
180+ @ Override
181+ public List <String > getValues (String key ) {
182+ return Collections .emptyList ();
183+ }
184+
185+ @ Override
186+ public Map <String , String > getOptions () {
187+ log .debug ("getOptions: {}:{}" , hostName , port );
188+ if (hostName .equals (gitUri .getHost ())) {
189+ return Collections .singletonMap (SshConstants .STRICT_HOST_KEY_CHECKING , SshConstants .NO );
190+ }
191+ else {
192+ return Collections .emptyMap ();
193+ }
194+ }
195+
196+ @ Override
197+ public Map <String , List <String >> getMultiValuedOptions () {
198+ return Collections .emptyMap ();
199+ }
200+ };
201+ }
202+
203+ @ Override
204+ public HostConfig lookupDefault (String hostName , int port , String userName ) {
205+ return lookup (hostName , port , userName );
206+ }
96207 }
97208
98209 /**
@@ -183,4 +294,13 @@ private CredentialsProvider getCachedCredentialsProvider() {
183294 }
184295 return credentialsProvider ;
185296 }
297+
298+ @ Override
299+ protected LsRemoteCommand lsRemoteCommand () {
300+ return authenticate (Git .lsRemoteRepository ());
301+ }
302+
303+ private CloneCommand cloneCommand () {
304+ return authenticate (Git .cloneRepository ());
305+ }
186306}
0 commit comments