Project

General

Profile

Download (9.61 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / test / world_wide_library.py @ 1c65dd5c

1
# SPDX-License-Identifier: AGPL-3.0-or-later
2

    
3
"""
4
Our helpful little stand-in for the Internet
5
"""
6

    
7
# This file is part of Haketilo.
8
#
9
# Copyright (C) 2021 jahoti <jahoti@tilde.team>
10
# Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org>
11
#
12
# This program is free software: you can redistribute it and/or modify
13
# it under the terms of the GNU Affero General Public License as
14
# published by the Free Software Foundation, either version 3 of the
15
# License, or (at your option) any later version.
16
#
17
# This program is distributed in the hope that it will be useful,
18
# but WITHOUT ANY WARRANTY; without even the implied warranty of
19
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
# GNU Affero General Public License for more details.
21
#
22
# You should have received a copy of the GNU Affero General Public License
23
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
24
#
25
#
26
# I, Wojtek Kosior, thereby promise not to sue for violation of this
27
# file's license. Although I request that you do not make use of this code
28
# in a proprietary program, I am not going to enforce this in court.
29

    
30
from hashlib import sha256
31
from pathlib import Path
32
from shutil import rmtree
33
from threading import Lock
34
from uuid import uuid4
35
import json
36

    
37
from .misc_constants import here
38
from .unit.utils import * # sample repo data
39

    
40
# TODO: instead of having the entire catalog defined here, make it possible to
41
# add catalog items from within individual test files.
42

    
43
served_scripts = {}
44
served_scripts_lock = Lock()
45

    
46
def start_serving_script(script_text):
47
    """
48
    Register given script so that it is served at
49
    https://serve.scrip.ts/?sha256=<script's_sha256_sum>
50

    
51
    Returns the URL at which script will be served.
52

    
53
    This function lacks thread safety. Might moght consider fixing this if it
54
    turns
55
    """
56
    sha256sum = sha256(script_text.encode()).digest().hex()
57
    served_scripts_lock.acquire()
58
    served_scripts[sha256sum] = script_text
59
    served_scripts_lock.release()
60

    
61
    return f'https://serve.scrip.ts/?sha256={sha256sum}'
62

    
63
def serve_script(command, get_params, post_params):
64
    """
65
    info() callback to pass to request-handling code in server.py. Facilitates
66
    serving scripts that have been registered with start_serving_script().
67
    """
68
    served_scripts_lock.acquire()
69
    try:
70
        script = served_scripts.get(get_params['sha256'][0])
71
    finally:
72
        served_scripts_lock.release()
73
    if script is None:
74
        return 404, {}, b''
75

    
76
    return 200, {'Content-Type': 'application/javascript'}, script
77

    
78
def dump_scripts(directory='./injected_scripts'):
79
    """
80
    Write all scripts that have been registered with start_serving_script()
81
    under the provided directory. If the directory already exists, it is wiped
82
    beforehand. If it doesn't exist, it is created.
83
    """
84
    directory = Path(directory)
85
    rmtree(directory, ignore_errors=True)
86
    directory.mkdir(parents=True)
87

    
88
    served_scripts_lock.acquire()
89
    for sha256, script in served_scripts.items():
90
        with open(directory / sha256, 'wt') as file:
91
            file.write(script)
92
    served_scripts_lock.release()
93

    
94
some_data = '{"some": "data"}'
95

    
96
# used by handler function of https://counterdoma.in
97
request_counter = 0
98

    
99
def serve_counter(command, get_params, post_params):
100
    global request_counter
101
    request_counter += 1
102
    return (
103
        200,
104
        {'Cache-Control': 'private, max-age=0, no-store'},
105
        json.dumps({'counter': request_counter})
106
    )
107

    
108
# Mock a Hydrilla repository.
109

    
110
make_handler = lambda txt: lambda c, g, p: (200, {}, txt)
111

    
112
# Mock files in the repository.
113
sample_contents = [f'Mi povas manĝi vitron, ĝi ne damaĝas min {i}'
114
                   for i in range(9)]
115
sample_hashes = [sha256(c.encode()).digest().hex() for c in sample_contents]
116

    
117
file_url = lambda hashed: f'https://hydril.la/file/sha256/{hashed}'
118

    
119
sample_files_catalog = dict([(file_url(h), make_handler(c))
120
                             for h, c in zip(sample_hashes, sample_contents)])
121

    
122
# Mock resources and mappings in the repository.
123
sample_resource_templates = []
124

    
125
for deps in [(0, 1, 2, 3), (3, 4, 5, 6), (6, 7, 8, 9)]:
126
    letters = [chr(ord('a') + i) for i in deps]
127
    sample_resource_templates.append({
128
        'id_suffix':    ''.join(letters),
129
        'files_count':  deps[0],
130
        'dependencies': [f'resource_{l}' for l in letters]
131
    })
132

    
133
suffixes = [srt['id_suffix'] for srt in sample_resource_templates]
134
sample_resource_templates.append({
135
    'id_suffix':    '-'.join(suffixes),
136
    'files_count':  2,
137
    'dependencies': [f'resource_{suffix}' for suffix in suffixes]
138
})
139

    
140
for i in range(10):
141
    sample_resource_templates.append({
142
        'id_suffix':    chr(ord('a') + i),
143
        'files_count':  i,
144
        'dependencies': []
145
    })
146

    
147
sample_resources_catalog = {}
148
sample_mappings_catalog = {}
149
sample_queries = {}
150

    
151
for srt in sample_resource_templates:
152
    resource = make_sample_resource()
153
    resource['api_schema_version']  = [1]
154
    resource['api_schema_revision'] = 1
155
    resource['identifier']          = f'resource_{srt["id_suffix"]}'
156
    resource['long_name']           = resource['identifier'].upper()
157
    resource['uuid']                = str(uuid4())
158
    resource['dependencies']        = srt['dependencies']
159
    resource['source_copyright']    = []
160
    resource['scripts']             = []
161
    for i in range(srt['files_count']):
162
        file_ref = {'file': f'file_{i}', 'sha256': sample_hashes[i]}
163
        resource[('source_copyright', 'scripts')[i & 1]].append(file_ref)
164

    
165
    resource_versions = [resource['version'], resource['version'].copy()]
166
    resource_versions[1][-1] += 1
167

    
168
    mapping = make_sample_mapping()
169
    mapping['api_schema_version']  = [1]
170
    mapping['api_schema_revision'] = 1
171
    mapping['identifier']          = f'mapping_{srt["id_suffix"]}'
172
    mapping['long_name']           = mapping['identifier'].upper()
173
    mapping['uuid']                = str(uuid4())
174
    mapping['source_copyright']    = resource['source_copyright']
175

    
176
    mapping_versions = [mapping['version'], mapping['version'].copy()]
177
    mapping_versions[1][-1] += 1
178

    
179
    sufs = [srt["id_suffix"], *[l for l in srt["id_suffix"] if l.isalpha()]]
180
    patterns = [f'https://example_{suf}.com/*' for suf in set(sufs)]
181
    payloads = {}
182

    
183
    for pat in patterns:
184
        payloads[pat] = {'identifier': resource['identifier']}
185

    
186
        queryable_url = pat.replace('*', 'something')
187
        if queryable_url not in sample_queries:
188
            sample_queries[queryable_url] = []
189

    
190
        sample_queries[queryable_url].append({
191
            'identifier': mapping['identifier'],
192
            'long_name':  mapping['long_name'],
193
            'version':    mapping_versions[1]
194
        })
195

    
196
    mapping['payloads'] = payloads
197

    
198
    for item, versions, catalog in [
199
        (resource, resource_versions, sample_resources_catalog),
200
        (mapping,  mapping_versions,  sample_mappings_catalog)
201
    ]:
202
        fmt = f'https://hydril.la/{item["type"]}/{item["identifier"]}%s.json'
203
        # Make 2 versions of each item so that we can test updates.
204
        for ver in versions:
205
            item['version'] = ver
206
            for fmt_arg in ('', '/' + item_version_string(item)):
207
                catalog[fmt % fmt_arg] = make_handler(json.dumps(item))
208

    
209
def serve_query(command, get_params, post_params):
210
    response = {
211
        'api_schema_version':  [1],
212
        'api_schema_revision': 1,
213
        'mappings':            sample_queries[get_params['url'][0]]
214
    }
215

    
216
    return (200, {}, json.dumps(response))
217

    
218
sample_queries_catalog = dict([(f'https://hydril.la/{suf}query', serve_query)
219
                               for suf in ('', '1/', '2/', '3/', '4/')])
220

    
221
catalog = {
222
    'http://gotmyowndoma.in':
223
    (302, {'location': 'http://gotmyowndoma.in/index.html'}, None),
224
    'http://gotmyowndoma.in/':
225
    (302, {'location': 'http://gotmyowndoma.in/index.html'}, None),
226
    'http://gotmyowndoma.in/index.html':
227
    (200, {}, here / 'data' / 'pages' / 'gotmyowndomain.html'),
228

    
229
    'https://gotmyowndoma.in':
230
    (302, {'location': 'https://gotmyowndoma.in/index.html'}, None),
231
    'https://gotmyowndoma.in/':
232
    (302, {'location': 'https://gotmyowndoma.in/index.html'}, None),
233
    'https://gotmyowndoma.in/index.html':
234
    (200, {}, here / 'data' / 'pages' / 'gotmyowndomain_https.html'),
235

    
236
    'https://gotmyowndoma.in/scripts_to_block_1.html':
237
    (200, {}, here / 'data' / 'pages' / 'scripts_to_block_1.html'),
238

    
239
    'https://anotherdoma.in/resource/blocked/by/CORS.json':
240
    lambda command, get_params, post_params: (200, {}, some_data),
241

    
242
    'https://counterdoma.in/': serve_counter,
243

    
244
    'https://serve.scrip.ts/': serve_script,
245

    
246
    'https://site.with.scripts.block.ed':
247
    (302, {'location': 'https://site.with.scripts.block.ed/index.html'}, None),
248
    'https://site.with.scripts.block.ed/':
249
    (302, {'location': 'https://site.with.scripts.block.ed/index.html'}, None),
250
    'https://site.with.scripts.block.ed/index.html':
251
    (200, {}, here / 'data' / 'pages' / 'gotmyowndomain_https.html'),
252

    
253
    'https://site.with.scripts.allow.ed':
254
    (302, {'location': 'https://site.with.scripts.allow.ed/index.html'}, None),
255
    'https://site.with.scripts.allow.ed/':
256
    (302, {'location': 'https://site.with.scripts.allow.ed/index.html'}, None),
257
    'https://site.with.scripts.allow.ed/index.html':
258
    (200, {}, here / 'data' / 'pages' / 'gotmyowndomain_https.html'),
259

    
260
    'https://site.with.paylo.ad':
261
    (302, {'location': 'https://site.with.paylo.ad/index.html'}, None),
262
    'https://site.with.paylo.ad/':
263
    (302, {'location': 'https://site.with.paylo.ad/index.html'}, None),
264
    'https://site.with.paylo.ad/index.html':
265
    (200, {}, here / 'data' / 'pages' / 'gotmyowndomain_https.html'),
266

    
267
    **sample_files_catalog,
268
    **sample_resources_catalog,
269
    **sample_mappings_catalog,
270
    **sample_queries_catalog
271
}
(11-11/11)