1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.internal.transport.sshd;
44
45 import java.net.InetSocketAddress;
46 import java.util.ArrayList;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.concurrent.ConcurrentHashMap;
50
51 import org.apache.sshd.client.auth.keyboard.UserInteraction;
52 import org.apache.sshd.client.session.ClientSession;
53 import org.apache.sshd.common.session.Session;
54 import org.apache.sshd.common.session.SessionListener;
55 import org.eclipse.jgit.transport.CredentialItem;
56 import org.eclipse.jgit.transport.CredentialsProvider;
57 import org.eclipse.jgit.transport.SshConstants;
58 import org.eclipse.jgit.transport.URIish;
59
60
61
62
63
64 public class JGitUserInteraction implements UserInteraction {
65
66 private final CredentialsProvider provider;
67
68
69
70
71
72 private final Map<Session, SessionListener> ongoing = new ConcurrentHashMap<>();
73
74
75
76
77
78
79
80
81 public JGitUserInteraction(CredentialsProvider provider) {
82 this.provider = provider;
83 }
84
85 @Override
86 public boolean isInteractionAllowed(ClientSession session) {
87 return provider != null && provider.isInteractive();
88 }
89
90 @Override
91 public String[] interactive(ClientSession session, String name,
92 String instruction, String lang, String[] prompt, boolean[] echo) {
93
94 List<CredentialItem> items = new ArrayList<>();
95 int numberOfHiddenInputs = 0;
96 for (int i = 0; i < prompt.length; i++) {
97 boolean hidden = i < echo.length && !echo[i];
98 if (hidden) {
99 numberOfHiddenInputs++;
100 }
101 }
102
103
104
105
106
107 if (name != null && !name.isEmpty()) {
108 items.add(new CredentialItem.InformationalMessage(name));
109 }
110 if (instruction != null && !instruction.isEmpty()) {
111 items.add(new CredentialItem.InformationalMessage(instruction));
112 }
113 for (int i = 0; i < prompt.length; i++) {
114 boolean hidden = i < echo.length && !echo[i];
115 if (hidden && numberOfHiddenInputs == 1) {
116
117
118
119 items.add(new CredentialItem.Password());
120
121
122
123 } else {
124 items.add(new CredentialItem.StringType(prompt[i], hidden));
125 }
126 }
127 if (items.isEmpty()) {
128
129 return prompt;
130 }
131 URIish uri = toURI(session.getUsername(),
132 (InetSocketAddress) session.getConnectAddress());
133
134
135
136 if (numberOfHiddenInputs > 0) {
137 SessionListener listener = ongoing.get(session);
138 if (listener != null) {
139 provider.reset(uri);
140 } else {
141 listener = new SessionAuthMarker(ongoing);
142 ongoing.put(session, listener);
143 session.addSessionListener(listener);
144 }
145 }
146 if (provider.get(uri, items)) {
147 return items.stream().map(i -> {
148 if (i instanceof CredentialItem.Password) {
149 return new String(((CredentialItem.Password) i).getValue());
150 } else if (i instanceof CredentialItem.StringType) {
151 return ((CredentialItem.StringType) i).getValue();
152 }
153 return null;
154 }).filter(s -> s != null).toArray(String[]::new);
155 }
156
157
158
159
160
161
162
163
164 return null;
165 }
166
167 @Override
168 public String getUpdatedPassword(ClientSession session, String prompt,
169 String lang) {
170
171 return null;
172 }
173
174
175
176
177
178
179
180
181
182
183 public static URIish toURI(String userName, InetSocketAddress remote) {
184 String host = remote.getHostString();
185 int port = remote.getPort();
186 return new URIish()
187 .setScheme(SshConstants.SSH_SCHEME)
188 .setHost(host)
189 .setPort(port)
190 .setUser(userName);
191 }
192
193
194
195
196
197 private static class SessionAuthMarker implements SessionListener {
198
199 private final Map<Session, SessionListener> registered;
200
201 public SessionAuthMarker(Map<Session, SessionListener> registered) {
202 this.registered = registered;
203 }
204
205 @Override
206 public void sessionEvent(Session session, SessionListener.Event event) {
207 if (event == SessionListener.Event.Authenticated) {
208 session.removeSessionListener(this);
209 registered.remove(session, this);
210 }
211 }
212
213 @Override
214 public void sessionClosed(Session session) {
215 session.removeSessionListener(this);
216 registered.remove(session, this);
217 }
218 }
219 }