Project

General

Profile

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

haketilo / test / world_wide_library.py @ 7218849a

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
# Mock files in the repository.
111
sample_contents = [f'Mi povas manĝi vitron, ĝi ne damaĝas min {i}'
112
                   for i in range(9)]
113
sample_hashes   = [sha256(c.encode()).digest().hex() for c in sample_contents]
114

    
115
file_url     = lambda hashed:   f'https://hydril.la/file/sha256-{hashed}'
116
file_handler = lambda contents: lambda c, g, p: (200, {}, contents)
117

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

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

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

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

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

    
146
sample_resources_catalog = {}
147
sample_mappings_catalog = {}
148

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

    
163
    # Keeping it simple - just make one corresponding mapping for each resource.
164
    payloads = {'https://example.com/*': {'identifier': resource['identifier']}}
165

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

    
175
    make_handler = lambda txt: lambda c, g, p: (200, {}, txt)
176

    
177
    for item, catalog in [
178
        (resource, sample_resources_catalog),
179
        (mapping,  sample_mappings_catalog)
180
    ]:
181
        fmt = f'https://hydril.la/{item["type"]}/{item["identifier"]}%s.json'
182
        # Make 2 versions of each item so that we can test updates.
183
        for i in range(2):
184
            for fmt_arg in ('', '/' + item_version_string(item)):
185
                catalog[fmt % fmt_arg] = make_handler(json.dumps(item))
186
            item['version'][-1] += 1
187

    
188
catalog = {
189
    'http://gotmyowndoma.in':
190
    (302, {'location': 'http://gotmyowndoma.in/index.html'}, None),
191
    'http://gotmyowndoma.in/':
192
    (302, {'location': 'http://gotmyowndoma.in/index.html'}, None),
193
    'http://gotmyowndoma.in/index.html':
194
    (200, {}, here / 'data' / 'pages' / 'gotmyowndomain.html'),
195

    
196
    'https://gotmyowndoma.in':
197
    (302, {'location': 'https://gotmyowndoma.in/index.html'}, None),
198
    'https://gotmyowndoma.in/':
199
    (302, {'location': 'https://gotmyowndoma.in/index.html'}, None),
200
    'https://gotmyowndoma.in/index.html':
201
    (200, {}, here / 'data' / 'pages' / 'gotmyowndomain_https.html'),
202

    
203
    'https://gotmyowndoma.in/scripts_to_block_1.html':
204
    (200, {}, here / 'data' / 'pages' / 'scripts_to_block_1.html'),
205

    
206
    'https://anotherdoma.in/resource/blocked/by/CORS.json':
207
    lambda command, get_params, post_params: (200, {}, some_data),
208

    
209
    'https://counterdoma.in/': serve_counter,
210

    
211
    'https://serve.scrip.ts/': serve_script,
212

    
213
    'https://site.with.scripts.block.ed':
214
    (302, {'location': 'https://site.with.scripts.block.ed/index.html'}, None),
215
    'https://site.with.scripts.block.ed/':
216
    (302, {'location': 'https://site.with.scripts.block.ed/index.html'}, None),
217
    'https://site.with.scripts.block.ed/index.html':
218
    (200, {}, here / 'data' / 'pages' / 'gotmyowndomain_https.html'),
219

    
220
    'https://site.with.scripts.allow.ed':
221
    (302, {'location': 'https://site.with.scripts.allow.ed/index.html'}, None),
222
    'https://site.with.scripts.allow.ed/':
223
    (302, {'location': 'https://site.with.scripts.allow.ed/index.html'}, None),
224
    'https://site.with.scripts.allow.ed/index.html':
225
    (200, {}, here / 'data' / 'pages' / 'gotmyowndomain_https.html'),
226

    
227
    'https://site.with.paylo.ad':
228
    (302, {'location': 'https://site.with.paylo.ad/index.html'}, None),
229
    'https://site.with.paylo.ad/':
230
    (302, {'location': 'https://site.with.paylo.ad/index.html'}, None),
231
    'https://site.with.paylo.ad/index.html':
232
    (200, {}, here / 'data' / 'pages' / 'gotmyowndomain_https.html'),
233

    
234
    **sample_files_catalog,
235
    **sample_resources_catalog,
236
    **sample_mappings_catalog
237
}
(9-9/9)