git-authenticate: Add tests.
[jackhill/guix/guix.git] / tests / git-authenticate.scm
CommitLineData
c83eedba
LC
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 %ed25519-public-key-file
36 (search-path %load-path "tests/ed25519.key"))
37(define %ed25519-secret-key-file
38 (search-path %load-path "tests/ed25519.sec"))
39(define %ed25519bis-public-key-file
40 (search-path %load-path "tests/ed25519bis.key"))
41(define %ed25519bis-secret-key-file
42 (search-path %load-path "tests/ed25519bis.sec"))
43
44(define (read-openpgp-packet file)
45 (get-openpgp-packet
46 (open-bytevector-input-port
47 (call-with-input-file file read-radix-64))))
48
49(define key-fingerprint
50 (compose openpgp-format-fingerprint
51 openpgp-public-key-fingerprint
52 read-openpgp-packet))
53
54(define (key-id file)
55 (define id
56 (openpgp-public-key-id (read-openpgp-packet)))
57
58 (string-pad (number->string id 16) 16 #\0))
59
60(define (gpg+git-available?)
61 (and (which (git-command))
62 (which (gpg-command)) (which (gpgconf-command))))
63
64\f
65(test-begin "git-authenticate")
66
67(unless (which (git-command)) (test-skip 1))
68(test-assert "unsigned commits"
69 (with-temporary-git-repository directory
70 '((add "a.txt" "A")
71 (commit "first commit")
72 (add "b.txt" "B")
73 (commit "second commit"))
74 (with-repository directory repository
75 (let ((commit1 (find-commit repository "first"))
76 (commit2 (find-commit repository "second")))
77 (guard (c ((unsigned-commit-error? c)
78 (oid=? (git-authentication-error-commit c)
79 (commit-id commit1))))
80 (authenticate-commits repository (list commit1 commit2)
81 #:keyring-reference "master")
82 'failed)))))
83
84(unless (gpg+git-available?) (test-skip 1))
85(test-assert "signed commits, default authorizations"
86 (with-fresh-gnupg-setup (list %ed25519-public-key-file
87 %ed25519-secret-key-file)
88 (with-temporary-git-repository directory
89 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
90 get-string-all))
91 (commit "zeroth commit")
92 (add "a.txt" "A")
93 (commit "first commit"
94 (signer ,(key-fingerprint %ed25519-public-key-file)))
95 (add "b.txt" "B")
96 (commit "second commit"
97 (signer ,(key-fingerprint %ed25519-public-key-file))))
98 (with-repository directory repository
99 (let ((commit1 (find-commit repository "first"))
100 (commit2 (find-commit repository "second")))
101 (authenticate-commits repository (list commit1 commit2)
102 #:default-authorizations
103 (list (openpgp-public-key-fingerprint
104 (read-openpgp-packet
105 %ed25519-public-key-file)))
106 #:keyring-reference "master"))))))
107
108(unless (gpg+git-available?) (test-skip 1))
109(test-assert "signed commits, .guix-authorizations"
110 (with-fresh-gnupg-setup (list %ed25519-public-key-file
111 %ed25519-secret-key-file)
112 (with-temporary-git-repository directory
113 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
114 get-string-all))
115 (add ".guix-authorizations"
116 ,(object->string
117 `(authorizations (version 0)
118 ((,(key-fingerprint
119 %ed25519-public-key-file)
120 (name "Charlie"))))))
121 (commit "zeroth commit")
122 (add "a.txt" "A")
123 (commit "first commit"
124 (signer ,(key-fingerprint %ed25519-public-key-file)))
125 (add ".guix-authorizations"
126 ,(object->string `(authorizations (version 0) ()))) ;empty
127 (commit "second commit"
128 (signer ,(key-fingerprint %ed25519-public-key-file)))
129 (add "b.txt" "B")
130 (commit "third commit"
131 (signer ,(key-fingerprint %ed25519-public-key-file))))
132 (with-repository directory repository
133 (let ((commit1 (find-commit repository "first"))
134 (commit2 (find-commit repository "second"))
135 (commit3 (find-commit repository "third")))
136 ;; COMMIT1 and COMMIT2 are fine.
137 (and (authenticate-commits repository (list commit1 commit2)
138 #:keyring-reference "master")
139
140 ;; COMMIT3 is signed by an unauthorized key according to its
141 ;; parent's '.guix-authorizations' file.
142 (guard (c ((unauthorized-commit-error? c)
143 (and (oid=? (git-authentication-error-commit c)
144 (commit-id commit3))
145 (bytevector=?
146 (openpgp-public-key-fingerprint
147 (unauthorized-commit-error-signing-key c))
148 (openpgp-public-key-fingerprint
149 (read-openpgp-packet
150 %ed25519-public-key-file))))))
151 (authenticate-commits repository
152 (list commit1 commit2 commit3)
153 #:keyring-reference "master")
154 'failed)))))))
155
156(unless (gpg+git-available?) (test-skip 1))
157(test-assert "signed commits, .guix-authorizations, unauthorized merge"
158 (with-fresh-gnupg-setup (list %ed25519-public-key-file
159 %ed25519-secret-key-file
160 %ed25519bis-public-key-file
161 %ed25519bis-secret-key-file)
162 (with-temporary-git-repository directory
163 `((add "signer1.key"
164 ,(call-with-input-file %ed25519-public-key-file
165 get-string-all))
166 (add "signer2.key"
167 ,(call-with-input-file %ed25519bis-public-key-file
168 get-string-all))
169 (add ".guix-authorizations"
170 ,(object->string
171 `(authorizations (version 0)
172 ((,(key-fingerprint
173 %ed25519-public-key-file)
174 (name "Alice"))))))
175 (commit "zeroth commit")
176 (add "a.txt" "A")
177 (commit "first commit"
178 (signer ,(key-fingerprint %ed25519-public-key-file)))
179 (branch "devel")
180 (checkout "devel")
181 (add "devel/1.txt" "1")
182 (commit "first devel commit"
183 (signer ,(key-fingerprint %ed25519bis-public-key-file)))
184 (checkout "master")
185 (add "b.txt" "B")
186 (commit "second commit"
187 (signer ,(key-fingerprint %ed25519-public-key-file)))
188 (merge "devel" "merge"
189 (signer ,(key-fingerprint %ed25519-public-key-file))))
190 (with-repository directory repository
191 (let ((master1 (find-commit repository "first commit"))
192 (master2 (find-commit repository "second commit"))
193 (devel1 (find-commit repository "first devel commit"))
194 (merge (find-commit repository "merge")))
195 (define (correct? c commit)
196 (and (oid=? (git-authentication-error-commit c)
197 (commit-id commit))
198 (bytevector=?
199 (openpgp-public-key-fingerprint
200 (unauthorized-commit-error-signing-key c))
201 (openpgp-public-key-fingerprint
202 (read-openpgp-packet %ed25519bis-public-key-file)))))
203
204 (and (authenticate-commits repository (list master1 master2)
205 #:keyring-reference "master")
206
207 ;; DEVEL1 is signed by an unauthorized key according to its
208 ;; parent's '.guix-authorizations' file.
209 (guard (c ((unauthorized-commit-error? c)
210 (correct? c devel1)))
211 (authenticate-commits repository
212 (list master1 devel1)
213 #:keyring-reference "master")
214 #f)
215
216 ;; MERGE is authorized but one of its ancestors is not.
217 (guard (c ((unauthorized-commit-error? c)
218 (correct? c devel1)))
219 (authenticate-commits repository
220 (list master1 master2
221 devel1 merge)
222 #:keyring-reference "master")
223 #f)))))))
224
225(unless (gpg+git-available?) (test-skip 1))
226(test-assert "signed commits, .guix-authorizations, authorized merge"
227 (with-fresh-gnupg-setup (list %ed25519-public-key-file
228 %ed25519-secret-key-file
229 %ed25519bis-public-key-file
230 %ed25519bis-secret-key-file)
231 (with-temporary-git-repository directory
232 `((add "signer1.key"
233 ,(call-with-input-file %ed25519-public-key-file
234 get-string-all))
235 (add "signer2.key"
236 ,(call-with-input-file %ed25519bis-public-key-file
237 get-string-all))
238 (add ".guix-authorizations"
239 ,(object->string
240 `(authorizations (version 0)
241 ((,(key-fingerprint
242 %ed25519-public-key-file)
243 (name "Alice"))))))
244 (commit "zeroth commit")
245 (add "a.txt" "A")
246 (commit "first commit"
247 (signer ,(key-fingerprint %ed25519-public-key-file)))
248 (branch "devel")
249 (checkout "devel")
250 (add ".guix-authorizations"
251 ,(object->string ;add the second signer
252 `(authorizations (version 0)
253 ((,(key-fingerprint
254 %ed25519-public-key-file)
255 (name "Alice"))
256 (,(key-fingerprint
257 %ed25519bis-public-key-file))))))
258 (commit "first devel commit"
259 (signer ,(key-fingerprint %ed25519-public-key-file)))
260 (add "devel/2.txt" "2")
261 (commit "second devel commit"
262 (signer ,(key-fingerprint %ed25519bis-public-key-file)))
263 (checkout "master")
264 (add "b.txt" "B")
265 (commit "second commit"
266 (signer ,(key-fingerprint %ed25519-public-key-file)))
267 (merge "devel" "merge"
268 (signer ,(key-fingerprint %ed25519-public-key-file)))
269 ;; After the merge, the second signer is authorized.
270 (add "c.txt" "C")
271 (commit "third commit"
272 (signer ,(key-fingerprint %ed25519bis-public-key-file))))
273 (with-repository directory repository
274 (let ((master1 (find-commit repository "first commit"))
275 (master2 (find-commit repository "second commit"))
276 (devel1 (find-commit repository "first devel commit"))
277 (devel2 (find-commit repository "second devel commit"))
278 (merge (find-commit repository "merge"))
279 (master3 (find-commit repository "third commit")))
280 (authenticate-commits repository
281 (list master1 master2 devel1 devel2
282 merge master3)
283 #:keyring-reference "master"))))))
284
285(test-end "git-authenticate")
286