chiark / gitweb /
Merge branch 'rational-jarsigner-logging' into 'master'
[fdroidserver.git] / tests / index.TestCase
1 #!/usr/bin/env python3
2
3 import inspect
4 import logging
5 import optparse
6 import os
7 import sys
8 import unittest
9 import zipfile
10 from unittest.mock import patch
11 import requests
12 import tempfile
13 import json
14 import shutil
15
16 localmodule = os.path.realpath(
17     os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
18 print('localmodule: ' + localmodule)
19 if localmodule not in sys.path:
20     sys.path.insert(0, localmodule)
21
22 import fdroidserver.common
23 import fdroidserver.index
24 import fdroidserver.signindex
25 import fdroidserver.publish
26 from testcommon import TmpCwd
27
28
29 GP_FINGERPRINT = 'B7C2EEFD8DAC7806AF67DFCD92EB18126BC08312A7F2D6F3862E46013C7A6135'
30
31
32 class IndexTest(unittest.TestCase):
33
34     def setUp(self):
35         logging.basicConfig(level=logging.DEBUG)
36         self.basedir = os.path.join(localmodule, 'tests')
37         self.tmpdir = os.path.abspath(os.path.join(self.basedir, '..', '.testfiles'))
38         if not os.path.exists(self.tmpdir):
39             os.makedirs(self.tmpdir)
40         os.chdir(self.basedir)
41
42         fdroidserver.common.config = None
43         config = fdroidserver.common.read_config(fdroidserver.common.options)
44         config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
45         fdroidserver.common.config = config
46         fdroidserver.signindex.config = config
47
48     def test_get_public_key_from_jar_succeeds(self):
49         source_dir = os.path.join(self.basedir, 'signindex')
50         for f in ('testy.jar', 'guardianproject.jar'):
51             testfile = os.path.join(source_dir, f)
52             jar = zipfile.ZipFile(testfile)
53             _, fingerprint = fdroidserver.index.get_public_key_from_jar(jar)
54             # comparing fingerprints should be sufficient
55             if f == 'testy.jar':
56                 self.assertTrue(fingerprint ==
57                                 '818E469465F96B704E27BE2FEE4C63AB' +
58                                 '9F83DDF30E7A34C7371A4728D83B0BC1')
59             if f == 'guardianproject.jar':
60                 self.assertTrue(fingerprint == GP_FINGERPRINT)
61
62     def test_get_public_key_from_jar_fails(self):
63         source_dir = os.path.join(self.basedir, 'signindex')
64         testfile = os.path.join(source_dir, 'unsigned.jar')
65         jar = zipfile.ZipFile(testfile)
66         with self.assertRaises(fdroidserver.index.VerificationException):
67             fdroidserver.index.get_public_key_from_jar(jar)
68
69     def test_download_repo_index_no_fingerprint(self):
70         with self.assertRaises(fdroidserver.index.VerificationException):
71             fdroidserver.index.download_repo_index("http://example.org")
72
73     def test_download_repo_index_no_jar(self):
74         with self.assertRaises(requests.exceptions.HTTPError):
75             fdroidserver.index.download_repo_index("http://example.org?fingerprint=nope")
76
77     @patch('requests.head')
78     def test_download_repo_index_same_etag(self, head):
79         url = 'http://example.org?fingerprint=test'
80         etag = '"4de5-54d840ce95cb9"'
81
82         head.return_value.headers = {'ETag': etag}
83         index, new_etag = fdroidserver.index.download_repo_index(url, etag=etag)
84
85         self.assertIsNone(index)
86         self.assertEqual(etag, new_etag)
87
88     @patch('requests.get')
89     @patch('requests.head')
90     def test_download_repo_index_new_etag(self, head, get):
91         url = 'http://example.org?fingerprint=' + GP_FINGERPRINT
92         etag = '"4de5-54d840ce95cb9"'
93
94         # fake HTTP answers
95         head.return_value.headers = {'ETag': 'new_etag'}
96         get.return_value.headers = {'ETag': 'new_etag'}
97         get.return_value.status_code = 200
98         testfile = os.path.join(os.path.dirname(__file__), 'signindex', 'guardianproject-v1.jar')
99         with open(testfile, 'rb') as file:
100             get.return_value.content = file.read()
101
102         index, new_etag = fdroidserver.index.download_repo_index(url, etag=etag)
103
104         # assert that the index was retrieved properly
105         self.assertEqual('Guardian Project Official Releases', index['repo']['name'])
106         self.assertEqual(GP_FINGERPRINT, index['repo']['fingerprint'])
107         self.assertTrue(len(index['repo']['pubkey']) > 500)
108         self.assertEqual(10, len(index['apps']))
109         self.assertEqual(10, len(index['packages']))
110         self.assertEqual('new_etag', new_etag)
111
112     def test_v1_sort_packages(self):
113
114         i = [{'packageName': 'org.smssecure.smssecure',
115               'apkName': 'org.smssecure.smssecure_134.apk',
116               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
117               'versionCode': 134},
118              {'packageName': 'org.smssecure.smssecure',
119               'apkName': 'org.smssecure.smssecure_134_b30bb97.apk',
120               'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
121               'versionCode': 134},
122              {'packageName': 'b075b32b4ef1e8a869e00edb136bd48e34a0382b85ced8628f164d1199584e4e'},
123              {'packageName': '43af70d1aca437c2f9974c4634cc5abe45bdc4d5d71529ac4e553488d3bb3ff6'},
124              {'packageName': 'org.smssecure.smssecure',
125               'apkName': 'org.smssecure.smssecure_135_b30bb97.apk',
126               'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
127               'versionCode': 135},
128              {'packageName': 'org.smssecure.smssecure',
129               'apkName': 'org.smssecure.smssecure_135.apk',
130               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
131               'versionCode': 135},
132              {'packageName': 'org.smssecure.smssecure',
133               'apkName': 'org.smssecure.smssecure_133.apk',
134               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
135               'versionCode': 133},
136              {'packageName': 'org.smssecure.smssecure',
137               'apkName': 'smssecure-weird-version.apk',
138               'signer': '99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff',
139               'versionCode': 133},
140              {'packageName': 'org.smssecure.smssecure',
141               'apkName': 'smssecure-custom.apk',
142               'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
143               'versionCode': 133},
144              {'packageName': 'org.smssecure.smssecure',
145               'apkName': 'smssecure-new-custom.apk',
146               'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
147               'versionCode': 135}]
148
149         o = [{'packageName': '43af70d1aca437c2f9974c4634cc5abe45bdc4d5d71529ac4e553488d3bb3ff6'},
150              {'packageName': 'b075b32b4ef1e8a869e00edb136bd48e34a0382b85ced8628f164d1199584e4e'},
151              # app test data
152              # # packages with reproducible developer signature
153              {'packageName': 'org.smssecure.smssecure',
154               'apkName': 'org.smssecure.smssecure_135_b30bb97.apk',
155               'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
156               'versionCode': 135},
157              {'packageName': 'org.smssecure.smssecure',
158               'apkName': 'org.smssecure.smssecure_134_b30bb97.apk',
159               'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
160               'versionCode': 134},
161              # # packages build and signed by fdroid
162              {'packageName': 'org.smssecure.smssecure',
163               'apkName': 'org.smssecure.smssecure_135.apk',
164               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
165               'versionCode': 135},
166              {'packageName': 'org.smssecure.smssecure',
167               'apkName': 'org.smssecure.smssecure_134.apk',
168               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
169               'versionCode': 134},
170              {'packageName': 'org.smssecure.smssecure',
171               'apkName': 'org.smssecure.smssecure_133.apk',
172               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
173               'versionCode': 133},
174              # # packages signed with unkown keys
175              {'packageName': 'org.smssecure.smssecure',
176               'apkName': 'smssecure-new-custom.apk',
177               'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
178               'versionCode': 135},
179              {'packageName': 'org.smssecure.smssecure',
180               'apkName': 'smssecure-custom.apk',
181               'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
182               'versionCode': 133},
183              {'packageName': 'org.smssecure.smssecure',
184               'apkName': 'smssecure-weird-version.apk',
185               'signer': '99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff',
186               'versionCode': 133}]
187
188         fdroidserver.common.config = {}
189         fdroidserver.common.fill_config_defaults(fdroidserver.common.config)
190         fdroidserver.publish.config = fdroidserver.common.config
191         fdroidserver.publish.config['keystorepass'] = '123456'
192         fdroidserver.publish.config['keypass'] = '123456'
193         fdroidserver.publish.config['keystore'] = os.path.join(os.getcwd(),
194                                                                'dummy-keystore.jks')
195         fdroidserver.publish.config['repo_keyalias'] = 'repokey'
196
197         testsmetadir = os.path.join(os.getcwd(), 'metadata')
198         with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
199             shutil.copytree(testsmetadir, 'metadata')
200             sigkeyfps = {
201                 "org.smssecure.smssecure": {
202                     "signer": "b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6"
203                 }
204             }
205             os.makedirs('stats')
206             jarfile = 'stats/publishsigkeys.jar'
207             with zipfile.ZipFile(jarfile, 'w', zipfile.ZIP_DEFLATED) as jar:
208                 jar.writestr('publishsigkeys.json', json.dumps(sigkeyfps))
209             fdroidserver.publish.sign_sig_key_fingerprint_list(jarfile)
210             with open('config.py', 'w'):
211                 pass
212
213             fdroidserver.index.v1_sort_packages(
214                 i, 'repo', fdroidserver.common.load_stats_fdroid_signing_key_fingerprints())
215             self.maxDiff = None
216             self.assertEqual(json.dumps(i, indent=2), json.dumps(o, indent=2))
217
218
219 if __name__ == "__main__":
220     parser = optparse.OptionParser()
221     parser.add_option("-v", "--verbose", action="store_true", default=False,
222                       help="Spew out even more information than normal")
223     (fdroidserver.common.options, args) = parser.parse_args(['--verbose'])
224
225     newSuite = unittest.TestSuite()
226     newSuite.addTest(unittest.makeSuite(IndexTest))
227     unittest.main(failfast=False)