1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.api;
11
12 import java.io.IOException;
13 import java.util.Arrays;
14 import java.util.Collection;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Map;
18 import java.util.Set;
19
20 import org.eclipse.jgit.annotations.NonNull;
21 import org.eclipse.jgit.api.errors.JGitInternalException;
22 import org.eclipse.jgit.api.errors.ServiceUnavailableException;
23 import org.eclipse.jgit.api.errors.WrongObjectTypeException;
24 import org.eclipse.jgit.errors.MissingObjectException;
25 import org.eclipse.jgit.internal.JGitText;
26 import org.eclipse.jgit.lib.Constants;
27 import org.eclipse.jgit.lib.GpgConfig;
28 import org.eclipse.jgit.lib.GpgSignatureVerifier;
29 import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
30 import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
31 import org.eclipse.jgit.lib.ObjectId;
32 import org.eclipse.jgit.lib.Repository;
33 import org.eclipse.jgit.revwalk.RevObject;
34 import org.eclipse.jgit.revwalk.RevWalk;
35
36
37
38
39
40
41 public class VerifySignatureCommand extends GitCommand<Map<String, VerificationResult>> {
42
43
44
45
46
47 public enum VerifyMode {
48
49
50
51 ANY,
52
53
54
55
56 COMMITS,
57
58
59
60
61 TAGS
62 }
63
64 private final Set<String> namesToCheck = new HashSet<>();
65
66 private VerifyMode mode = VerifyMode.ANY;
67
68 private GpgSignatureVerifier verifier;
69
70 private GpgConfig config;
71
72 private boolean ownVerifier;
73
74
75
76
77
78
79
80 public VerifySignatureCommand(Repository repo) {
81 super(repo);
82 }
83
84
85
86
87
88
89
90
91
92
93 public VerifySignatureCommand addName(String name) {
94 checkCallable();
95 namesToCheck.add(name);
96 return this;
97 }
98
99
100
101
102
103
104
105
106
107
108 public VerifySignatureCommand addNames(String... names) {
109 checkCallable();
110 namesToCheck.addAll(Arrays.asList(names));
111 return this;
112 }
113
114
115
116
117
118
119
120
121
122
123 public VerifySignatureCommand addNames(Collection<String> names) {
124 checkCallable();
125 namesToCheck.addAll(names);
126 return this;
127 }
128
129
130
131
132
133
134
135
136 public VerifySignatureCommand setMode(@NonNull VerifyMode mode) {
137 checkCallable();
138 this.mode = mode;
139 return this;
140 }
141
142
143
144
145
146
147
148
149
150 public VerifySignatureCommand setVerifier(GpgSignatureVerifier verifier) {
151 checkCallable();
152 this.verifier = verifier;
153 return this;
154 }
155
156
157
158
159
160
161
162
163
164
165
166 public VerifySignatureCommand setGpgConfig(GpgConfig config) {
167 checkCallable();
168 this.config = config;
169 return this;
170 }
171
172
173
174
175
176
177
178 public GpgSignatureVerifier getVerifier() {
179 return verifier;
180 }
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 @Override
204 @NonNull
205 public Map<String, VerificationResult> call()
206 throws ServiceUnavailableException, WrongObjectTypeException {
207 checkCallable();
208 setCallable(false);
209 Map<String, VerificationResult> result = new HashMap<>();
210 if (verifier == null) {
211 GpgSignatureVerifierFactory factory = GpgSignatureVerifierFactory
212 .getDefault();
213 if (factory == null) {
214 throw new ServiceUnavailableException(
215 JGitText.get().signatureVerificationUnavailable);
216 }
217 verifier = factory.getVerifier();
218 ownVerifier = true;
219 }
220 if (config == null) {
221 config = new GpgConfig(repo.getConfig());
222 }
223 try (RevWalk walk = new RevWalk(repo)) {
224 for (String toCheck : namesToCheck) {
225 ObjectId id = repo.resolve(toCheck);
226 if (id != null && !ObjectId.zeroId().equals(id)) {
227 RevObject object;
228 try {
229 object = walk.parseAny(id);
230 } catch (MissingObjectException e) {
231 continue;
232 }
233 VerificationResult verification = verifyOne(object);
234 if (verification != null) {
235 result.put(toCheck, verification);
236 }
237 }
238 }
239 } catch (IOException e) {
240 throw new JGitInternalException(
241 JGitText.get().signatureVerificationError, e);
242 } finally {
243 if (ownVerifier) {
244 verifier.clear();
245 }
246 }
247 return result;
248 }
249
250 private VerificationResult verifyOne(RevObject object)
251 throws WrongObjectTypeException, IOException {
252 int type = object.getType();
253 if (VerifyMode.TAGS.equals(mode) && type != Constants.OBJ_TAG) {
254 throw new WrongObjectTypeException(object, Constants.OBJ_TAG);
255 } else if (VerifyMode.COMMITS.equals(mode)
256 && type != Constants.OBJ_COMMIT) {
257 throw new WrongObjectTypeException(object, Constants.OBJ_COMMIT);
258 }
259 if (type == Constants.OBJ_COMMIT || type == Constants.OBJ_TAG) {
260 try {
261 GpgSignatureVerifier.SignatureVerification verification = verifier
262 .verifySignature(object, config);
263 if (verification == null) {
264
265 return null;
266 }
267
268 return new Result(object, verification, null);
269 } catch (JGitInternalException e) {
270 return new Result(object, null, e);
271 }
272 }
273 return null;
274 }
275
276 private static class Result implements VerificationResult {
277
278 private final Throwable throwable;
279
280 private final SignatureVerification verification;
281
282 private final RevObject object;
283
284 public Result(RevObject object, SignatureVerification verification,
285 Throwable throwable) {
286 this.object = object;
287 this.verification = verification;
288 this.throwable = throwable;
289 }
290
291 @Override
292 public Throwable getException() {
293 return throwable;
294 }
295
296 @Override
297 public SignatureVerification getVerification() {
298 return verification;
299 }
300
301 @Override
302 public RevObject getObject() {
303 return object;
304 }
305
306 }
307 }