gnu: r-wgcna: Move to (gnu packages bioconductor).
[jackhill/guix/guix.git] / tests / git-authenticate.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
3 ;;;
4 ;;; This file is part of GNU Guix.
5 ;;;
6 ;;; GNU Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
10 ;;;
11 ;;; GNU Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
15 ;;;
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19 (define-module (test-git-authenticate)
20 #:use-module (git)
21 #:use-module (guix git)
22 #:use-module (guix git-authenticate)
23 #:use-module (guix openpgp)
24 #:use-module (guix tests git)
25 #:use-module (guix tests gnupg)
26 #:use-module (guix build utils)
27 #:use-module (srfi srfi-1)
28 #:use-module (srfi srfi-34)
29 #:use-module (srfi srfi-64)
30 #:use-module (rnrs bytevectors)
31 #:use-module (rnrs io ports))
32
33 ;; Test the (guix git-authenticate) tools.
34
35 (define (gpg+git-available?)
36 (and (which (git-command))
37 (which (gpg-command)) (which (gpgconf-command))))
38
39 \f
40 (test-begin "git-authenticate")
41
42 (unless (which (git-command)) (test-skip 1))
43 (test-assert "unsigned commits"
44 (with-temporary-git-repository directory
45 '((add "a.txt" "A")
46 (commit "first commit")
47 (add "b.txt" "B")
48 (commit "second commit"))
49 (with-repository directory repository
50 (let ((commit1 (find-commit repository "first"))
51 (commit2 (find-commit repository "second")))
52 (guard (c ((unsigned-commit-error? c)
53 (oid=? (git-authentication-error-commit c)
54 (commit-id commit1))))
55 (authenticate-commits repository (list commit1 commit2)
56 #:keyring-reference "master")
57 'failed)))))
58
59 (unless (gpg+git-available?) (test-skip 1))
60 (test-assert "signed commits, SHA1 signature"
61 (with-fresh-gnupg-setup (list %ed25519-public-key-file
62 %ed25519-secret-key-file)
63 ;; Force use of SHA1 for signatures.
64 (call-with-output-file (string-append (getenv "GNUPGHOME") "/gpg.conf")
65 (lambda (port)
66 (display "digest-algo sha1" port)))
67
68 (with-temporary-git-repository directory
69 `((add "a.txt" "A")
70 (add "signer.key" ,(call-with-input-file %ed25519-public-key-file
71 get-string-all))
72 (add ".guix-authorizations"
73 ,(object->string
74 `(authorizations (version 0)
75 ((,(key-fingerprint %ed25519-public-key-file)
76 (name "Charlie"))))))
77 (commit "first commit"
78 (signer ,(key-fingerprint %ed25519-public-key-file))))
79 (with-repository directory repository
80 (let ((commit (find-commit repository "first")))
81 (guard (c ((unsigned-commit-error? c)
82 (oid=? (git-authentication-error-commit c)
83 (commit-id commit))))
84 (authenticate-commits repository (list commit)
85 #:keyring-reference "master")
86 'failed))))))
87
88 (unless (gpg+git-available?) (test-skip 1))
89 (test-assert "signed commits, default authorizations"
90 (with-fresh-gnupg-setup (list %ed25519-public-key-file
91 %ed25519-secret-key-file)
92 (with-temporary-git-repository directory
93 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
94 get-string-all))
95 (commit "zeroth commit")
96 (add "a.txt" "A")
97 (commit "first commit"
98 (signer ,(key-fingerprint %ed25519-public-key-file)))
99 (add "b.txt" "B")
100 (commit "second commit"
101 (signer ,(key-fingerprint %ed25519-public-key-file))))
102 (with-repository directory repository
103 (let ((commit1 (find-commit repository "first"))
104 (commit2 (find-commit repository "second")))
105 (authenticate-commits repository (list commit1 commit2)
106 #:default-authorizations
107 (list (openpgp-public-key-fingerprint
108 (read-openpgp-packet
109 %ed25519-public-key-file)))
110 #:keyring-reference "master"))))))
111
112 (unless (gpg+git-available?) (test-skip 1))
113 (test-assert "signed commits, .guix-authorizations"
114 (with-fresh-gnupg-setup (list %ed25519-public-key-file
115 %ed25519-secret-key-file)
116 (with-temporary-git-repository directory
117 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
118 get-string-all))
119 (add ".guix-authorizations"
120 ,(object->string
121 `(authorizations (version 0)
122 ((,(key-fingerprint
123 %ed25519-public-key-file)
124 (name "Charlie"))))))
125 (commit "zeroth commit")
126 (add "a.txt" "A")
127 (commit "first commit"
128 (signer ,(key-fingerprint %ed25519-public-key-file)))
129 (add ".guix-authorizations"
130 ,(object->string `(authorizations (version 0) ()))) ;empty
131 (commit "second commit"
132 (signer ,(key-fingerprint %ed25519-public-key-file)))
133 (add "b.txt" "B")
134 (commit "third commit"
135 (signer ,(key-fingerprint %ed25519-public-key-file))))
136 (with-repository directory repository
137 (let ((commit1 (find-commit repository "first"))
138 (commit2 (find-commit repository "second"))
139 (commit3 (find-commit repository "third")))
140 ;; COMMIT1 and COMMIT2 are fine.
141 (and (authenticate-commits repository (list commit1 commit2)
142 #:keyring-reference "master")
143
144 ;; COMMIT3 is signed by an unauthorized key according to its
145 ;; parent's '.guix-authorizations' file.
146 (guard (c ((unauthorized-commit-error? c)
147 (and (oid=? (git-authentication-error-commit c)
148 (commit-id commit3))
149 (bytevector=?
150 (openpgp-public-key-fingerprint
151 (unauthorized-commit-error-signing-key c))
152 (openpgp-public-key-fingerprint
153 (read-openpgp-packet
154 %ed25519-public-key-file))))))
155 (authenticate-commits repository
156 (list commit1 commit2 commit3)
157 #:keyring-reference "master")
158 'failed)))))))
159
160 (unless (gpg+git-available?) (test-skip 1))
161 (test-assert "signed commits, .guix-authorizations, unauthorized merge"
162 (with-fresh-gnupg-setup (list %ed25519-public-key-file
163 %ed25519-secret-key-file
164 %ed25519bis-public-key-file
165 %ed25519bis-secret-key-file)
166 (with-temporary-git-repository directory
167 `((add "signer1.key"
168 ,(call-with-input-file %ed25519-public-key-file
169 get-string-all))
170 (add "signer2.key"
171 ,(call-with-input-file %ed25519bis-public-key-file
172 get-string-all))
173 (add ".guix-authorizations"
174 ,(object->string
175 `(authorizations (version 0)
176 ((,(key-fingerprint
177 %ed25519-public-key-file)
178 (name "Alice"))))))
179 (commit "zeroth commit")
180 (add "a.txt" "A")
181 (commit "first commit"
182 (signer ,(key-fingerprint %ed25519-public-key-file)))
183 (branch "devel")
184 (checkout "devel")
185 (add "devel/1.txt" "1")
186 (commit "first devel commit"
187 (signer ,(key-fingerprint %ed25519bis-public-key-file)))
188 (checkout "master")
189 (add "b.txt" "B")
190 (commit "second commit"
191 (signer ,(key-fingerprint %ed25519-public-key-file)))
192 (merge "devel" "merge"
193 (signer ,(key-fingerprint %ed25519-public-key-file))))
194 (with-repository directory repository
195 (let ((master1 (find-commit repository "first commit"))
196 (master2 (find-commit repository "second commit"))
197 (devel1 (find-commit repository "first devel commit"))
198 (merge (find-commit repository "merge")))
199 (define (correct? c commit)
200 (and (oid=? (git-authentication-error-commit c)
201 (commit-id commit))
202 (bytevector=?
203 (openpgp-public-key-fingerprint
204 (unauthorized-commit-error-signing-key c))
205 (openpgp-public-key-fingerprint
206 (read-openpgp-packet %ed25519bis-public-key-file)))))
207
208 (and (authenticate-commits repository (list master1 master2)
209 #:keyring-reference "master")
210
211 ;; DEVEL1 is signed by an unauthorized key according to its
212 ;; parent's '.guix-authorizations' file.
213 (guard (c ((unauthorized-commit-error? c)
214 (correct? c devel1)))
215 (authenticate-commits repository
216 (list master1 devel1)
217 #:keyring-reference "master")
218 #f)
219
220 ;; MERGE is authorized but one of its ancestors is not.
221 (guard (c ((unauthorized-commit-error? c)
222 (correct? c devel1)))
223 (authenticate-commits repository
224 (list master1 master2
225 devel1 merge)
226 #:keyring-reference "master")
227 #f)))))))
228
229 (unless (gpg+git-available?) (test-skip 1))
230 (test-assert "signed commits, .guix-authorizations, authorized merge"
231 (with-fresh-gnupg-setup (list %ed25519-public-key-file
232 %ed25519-secret-key-file
233 %ed25519bis-public-key-file
234 %ed25519bis-secret-key-file)
235 (with-temporary-git-repository directory
236 `((add "signer1.key"
237 ,(call-with-input-file %ed25519-public-key-file
238 get-string-all))
239 (add "signer2.key"
240 ,(call-with-input-file %ed25519bis-public-key-file
241 get-string-all))
242 (add ".guix-authorizations"
243 ,(object->string
244 `(authorizations (version 0)
245 ((,(key-fingerprint
246 %ed25519-public-key-file)
247 (name "Alice"))))))
248 (commit "zeroth commit")
249 (add "a.txt" "A")
250 (commit "first commit"
251 (signer ,(key-fingerprint %ed25519-public-key-file)))
252 (branch "devel")
253 (checkout "devel")
254 (add ".guix-authorizations"
255 ,(object->string ;add the second signer
256 `(authorizations (version 0)
257 ((,(key-fingerprint
258 %ed25519-public-key-file)
259 (name "Alice"))
260 (,(key-fingerprint
261 %ed25519bis-public-key-file))))))
262 (commit "first devel commit"
263 (signer ,(key-fingerprint %ed25519-public-key-file)))
264 (add "devel/2.txt" "2")
265 (commit "second devel commit"
266 (signer ,(key-fingerprint %ed25519bis-public-key-file)))
267 (checkout "master")
268 (add "b.txt" "B")
269 (commit "second commit"
270 (signer ,(key-fingerprint %ed25519-public-key-file)))
271 (merge "devel" "merge"
272 (signer ,(key-fingerprint %ed25519-public-key-file)))
273 ;; After the merge, the second signer is authorized.
274 (add "c.txt" "C")
275 (commit "third commit"
276 (signer ,(key-fingerprint %ed25519bis-public-key-file))))
277 (with-repository directory repository
278 (let ((master1 (find-commit repository "first commit"))
279 (master2 (find-commit repository "second commit"))
280 (devel1 (find-commit repository "first devel commit"))
281 (devel2 (find-commit repository "second devel commit"))
282 (merge (find-commit repository "merge"))
283 (master3 (find-commit repository "third commit")))
284 (authenticate-commits repository
285 (list master1 master2 devel1 devel2
286 merge master3)
287 #:keyring-reference "master"))))))
288
289 (unless (gpg+git-available?) (test-skip 1))
290 (test-assert "signed commits, .guix-authorizations removed"
291 (with-fresh-gnupg-setup (list %ed25519-public-key-file
292 %ed25519-secret-key-file)
293 (with-temporary-git-repository directory
294 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
295 get-string-all))
296 (add ".guix-authorizations"
297 ,(object->string
298 `(authorizations (version 0)
299 ((,(key-fingerprint
300 %ed25519-public-key-file)
301 (name "Charlie"))))))
302 (commit "zeroth commit")
303 (add "a.txt" "A")
304 (commit "first commit"
305 (signer ,(key-fingerprint %ed25519-public-key-file)))
306 (remove ".guix-authorizations")
307 (commit "second commit"
308 (signer ,(key-fingerprint %ed25519-public-key-file)))
309 (add "b.txt" "B")
310 (commit "third commit"
311 (signer ,(key-fingerprint %ed25519-public-key-file))))
312 (with-repository directory repository
313 (let ((commit1 (find-commit repository "first"))
314 (commit2 (find-commit repository "second"))
315 (commit3 (find-commit repository "third")))
316 ;; COMMIT1 and COMMIT2 are fine.
317 (and (authenticate-commits repository (list commit1 commit2)
318 #:keyring-reference "master")
319
320 ;; COMMIT3 is rejected because COMMIT2 removes
321 ;; '.guix-authorizations'.
322 (guard (c ((unauthorized-commit-error? c)
323 (oid=? (git-authentication-error-commit c)
324 (commit-id commit2))))
325 (authenticate-commits repository
326 (list commit1 commit2 commit3)
327 #:keyring-reference "master")
328 'failed)))))))
329
330 (test-end "git-authenticate")
331