SCAutolib 3.0.0__py3-none-any.whl → 3.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of SCAutolib might be problematic. Click here for more details.

SCAutolib/__init__.py CHANGED
@@ -27,6 +27,7 @@ LIB_DUMP = LIB_DIR.joinpath("dump")
27
27
  LIB_DUMP_USERS = LIB_DUMP.joinpath("users")
28
28
  LIB_DUMP_CAS = LIB_DUMP.joinpath("cas")
29
29
  LIB_DUMP_CARDS = LIB_DUMP.joinpath("cards")
30
+ LIB_DUMP_CONFS = LIB_DUMP.joinpath("confs")
30
31
 
31
32
 
32
33
  schema_cas = Schema(And(
SCAutolib/cli_commands.py CHANGED
@@ -154,6 +154,18 @@ def setup_user(ctx, name, card_dir, card_type, passwd, pin, user_type):
154
154
  exit(ReturnCode.SUCCESS.value)
155
155
 
156
156
 
157
+ @click.command()
158
+ @click.pass_context
159
+ def cleanup(ctx):
160
+ """
161
+ Cleanup all the configurations and system changes done by the prepare
162
+ command.
163
+ """
164
+ ctx.obj["CONTROLLER"].cleanup()
165
+ exit(ReturnCode.SUCCESS.value)
166
+
167
+
157
168
  cli.add_command(setup_ca)
158
169
  cli.add_command(prepare)
159
170
  cli.add_command(setup_user)
171
+ cli.add_command(cleanup)
SCAutolib/controller.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import os
2
3
  from pathlib import Path
3
4
  from schema import Schema, Use
4
5
  from shutil import rmtree
@@ -7,9 +8,10 @@ from typing import Union
7
8
  from SCAutolib import exceptions, schema_cas, schema_user, schema_card
8
9
  from SCAutolib import (logger, run, LIB_DIR, LIB_BACKUP, LIB_DUMP,
9
10
  LIB_DUMP_USERS, LIB_DUMP_CAS, LIB_DUMP_CARDS,
10
- TEMPLATES_DIR)
11
+ LIB_DUMP_CONFS, TEMPLATES_DIR)
11
12
  from SCAutolib.models import CA, file, user, card, authselect as auth
12
13
  from SCAutolib.models.file import File, OpensslCnf
14
+ from SCAutolib.models.CA import BaseCA
13
15
  from SCAutolib.enums import (OSVersion, CardType, UserType)
14
16
  from SCAutolib.utils import (_check_selinux, _gen_private_key,
15
17
  _get_os_version, _install_packages,
@@ -63,9 +65,15 @@ class Controller:
63
65
  self.lib_conf = self._validate_configuration(tmp_conf, params)
64
66
  self.users = []
65
67
  for d in (LIB_DIR, LIB_BACKUP, LIB_DUMP, LIB_DUMP_USERS, LIB_DUMP_CAS,
66
- LIB_DUMP_CARDS):
68
+ LIB_DUMP_CARDS, LIB_DUMP_CONFS):
67
69
  d.mkdir(exist_ok=True)
68
70
 
71
+ if LIB_DUMP_CAS.joinpath("local_ca.json").exists():
72
+ self.local_ca = BaseCA.load(LIB_DUMP_CAS.joinpath("local_ca.json"))
73
+
74
+ if LIB_DUMP_CAS.joinpath("ipa-server.json").exists():
75
+ self.ipa_ca = BaseCA.load(LIB_DUMP_CAS.joinpath("ipa-server.json"))
76
+
69
77
  def prepare(self, force: bool, gdm: bool, install_missing: bool,
70
78
  graphical: bool):
71
79
  """
@@ -390,28 +398,43 @@ class Controller:
390
398
  dump_to_json(card)
391
399
 
392
400
  def cleanup(self):
393
- # FIXME Card.delete is not yet implemented, sssd_conf.clean is broken,
394
- # authselect.cleanup is not implemented, local_ca.cleanup is not
395
- # sufficient
396
401
  """
397
402
  Clean the system after setup. This method restores the SSSD config file,
398
403
  deletes created users with cards, remove CA's (local and/or IPA Client)
399
404
  """
400
- self.sssd_conf.clean()
405
+ users = {}
401
406
 
402
407
  for user_file in LIB_DUMP_USERS.iterdir():
403
- usr = user.User.load(user_file)
404
- usr.delete_user()
408
+ usr = user.User.load(user_file, ipa_server=self.ipa_ca)
409
+ users[usr.username] = usr
410
+ if usr.username != "root":
411
+ usr.delete_user()
412
+
405
413
  for card_file in LIB_DUMP_CARDS.iterdir():
406
414
  if card_file.exists():
407
- card.Card.load(card_file).delete() # TODO implement card.delete
415
+ card_obj = card.Card.load(card_file)
416
+ if card_obj.card_type == CardType.virtual:
417
+ card_obj.user = users[card_obj.cardholder]
418
+ self.revoke_certs(card_obj)
419
+ card_obj.delete()
408
420
 
409
421
  if self.local_ca:
410
- self.local_ca.cleanup() # FIXME restore sssd_auth_ca_db.pem file
422
+ self.local_ca.cleanup()
423
+ self.local_ca.restore_ca_db()
411
424
  if self.ipa_ca:
412
425
  self.ipa_ca.cleanup()
413
426
 
414
- self.authselect.cleanup() # TODO implement authselect.cleanup
427
+ opensc_cache_dir = Path(os.path.expanduser('~') + "/.cache/opensc/")
428
+ if opensc_cache_dir.exists():
429
+ for cache_file in opensc_cache_dir.iterdir():
430
+ cache_file.unlink()
431
+ logger.debug("Removed opensc file cache")
432
+
433
+ self.sssd_conf.restore()
434
+ pcscd_service = File("/usr/lib/systemd/system/pcscd.service")
435
+ pcscd_service.restore()
436
+ opensc_module = File("/usr/share/p11-kit/modules/opensc.module")
437
+ opensc_module.restore()
415
438
 
416
439
  @staticmethod
417
440
  def _validate_configuration(conf: dict, params: {} = None) -> dict:
SCAutolib/models/CA.py CHANGED
@@ -16,7 +16,8 @@ from python_freeipa.client_meta import ClientMeta
16
16
  from shutil import rmtree, copy2
17
17
  from socket import gethostname
18
18
 
19
- from SCAutolib import TEMPLATES_DIR, logger, run, LIB_DIR, LIB_DUMP_CAS
19
+ from SCAutolib import TEMPLATES_DIR, logger, run, LIB_DIR, LIB_DUMP_CAS, \
20
+ LIB_BACKUP
20
21
  from SCAutolib.exceptions import SCAutolibException
21
22
  from SCAutolib.models.file import OpensslCnf
22
23
  from SCAutolib.enums import CAType
@@ -28,6 +29,7 @@ class BaseCA:
28
29
  _ca_cert: Path = None
29
30
  _ca_key: Path = None
30
31
  _ca_pki_db: Path = Path("/etc/sssd/pki/sssd_auth_ca_db.pem")
32
+ _ca_original_path: Path = LIB_BACKUP.joinpath("ca-db-original.backup")
31
33
 
32
34
  @property
33
35
  def cert(self):
@@ -68,6 +70,8 @@ class BaseCA:
68
70
  with self._ca_pki_db.open("a+") as f:
69
71
  f.seek(0)
70
72
  data = f.read()
73
+ with self._ca_original_path.open('w') as backup:
74
+ backup.write(data)
71
75
  if root_cert not in data:
72
76
  f.write(root_cert)
73
77
  else:
@@ -81,6 +85,22 @@ class BaseCA:
81
85
  run(f"restorecon -v {self._ca_pki_db}")
82
86
  logger.info("Local CA is updated")
83
87
 
88
+ def restore_ca_db(self):
89
+ """
90
+ restores /etc/sssd/pki/sssd_auth_ca_db.pem to the state it was before.
91
+ """
92
+ if self._ca_original_path.exists():
93
+ logger.debug("Found original version of sssd_auth_ca_db.pem")
94
+ with self._ca_original_path.open() as backup, \
95
+ self._ca_pki_db.open("w") as f:
96
+ f.write(backup.read())
97
+ self._ca_original_path.unlink()
98
+ else:
99
+ logger.debug("Original version of sssd_auth_ca_db.pem not found")
100
+ if self._ca_pki_db.exists():
101
+ self._ca_pki_db.unlink()
102
+ logger.info(f"Restored {self._ca_pki_db} to original version")
103
+
84
104
  def sign_cert(self):
85
105
  """
86
106
  Sign the certificate
@@ -309,6 +329,11 @@ class LocalCA(BaseCA):
309
329
  file.unlink()
310
330
  elif file.is_dir():
311
331
  shutil.rmtree(file)
332
+
333
+ if self.dump_file.exists():
334
+ self.dump_file.unlink()
335
+ logger.debug(f"Removed {self.dump_file} dump file")
336
+
312
337
  logger.info(f"Local CA from {self.root_dir} is removed")
313
338
 
314
339
 
SCAutolib/models/card.py CHANGED
@@ -6,6 +6,7 @@ that we are using in the library. Those types are: virtual smart card, real
6
6
  import json
7
7
  import re
8
8
  import time
9
+ import shutil
9
10
  from pathlib import Path
10
11
  from traceback import format_exc
11
12
 
@@ -315,6 +316,23 @@ class VirtualCard(Card):
315
316
 
316
317
  return self
317
318
 
319
+ def delete(self):
320
+ """
321
+ Deletes the virtual card directory which contains certs, SoftHSM2 token
322
+ and NSS database. Also removes the systemd service for virtual smart
323
+ card.
324
+ """
325
+ shutil.rmtree(self.card_dir)
326
+ logger.info(f"Virtual card dir of {self.name} removed")
327
+
328
+ self._service_location.unlink()
329
+ run("systemctl daemon-reload", sleep=3)
330
+ logger.debug(f"Service {self._service_name} was removed")
331
+
332
+ if self.dump_file.exists():
333
+ self.dump_file.unlink()
334
+ logger.debug(f"Removed {self.dump_file} dump file")
335
+
318
336
  def gen_csr(self):
319
337
  """
320
338
  Method for generating user specific CSR file that would be sent to the
SCAutolib/models/file.py CHANGED
@@ -22,8 +22,9 @@ from pathlib import Path
22
22
  from shutil import copy2
23
23
  from traceback import format_exc
24
24
  from typing import Union
25
+ import json
25
26
 
26
- from SCAutolib import logger, TEMPLATES_DIR, LIB_BACKUP, run
27
+ from SCAutolib import logger, TEMPLATES_DIR, LIB_BACKUP, LIB_DUMP_CONFS, run
27
28
  from SCAutolib.exceptions import SCAutolibException
28
29
 
29
30
 
@@ -47,7 +48,6 @@ class File:
47
48
  _template = None
48
49
  _default_parser = None
49
50
  _simple_content = None
50
- _backup: dict = dict()
51
51
 
52
52
  def __init__(self, filepath: Union[str, Path], template: Path = None):
53
53
  """
@@ -232,13 +232,21 @@ class File:
232
232
  "backup": str(new_path)}
233
233
  return new_path
234
234
 
235
- def restore(self):
235
+ def restore(self, name: str = None):
236
236
  """
237
237
  Copies backup file to original file location.
238
238
  """
239
- copy2(self._backup["backup"], self._backup["original"])
240
- logger.debug(f"File {self._backup['backup']} "
241
- f"is restored to {self._backup['original']}")
239
+ original_path = LIB_BACKUP.joinpath(
240
+ f"{name if name else self._conf_file.name}.backup")
241
+
242
+ if original_path.exists():
243
+ with self._conf_file.open("w") as config, \
244
+ original_path.open() as backup:
245
+ config.write(backup.read())
246
+ original_path.unlink()
247
+ logger.debug(
248
+ f"File {self._conf_file} is restored to {original_path}"
249
+ )
242
250
 
243
251
 
244
252
  class SSSDConf(File):
@@ -259,8 +267,12 @@ class SSSDConf(File):
259
267
  _conf_file = Path("/etc/sssd/sssd.conf")
260
268
  _backup_original = None
261
269
  _backup_default = LIB_BACKUP.joinpath('default-sssd.conf')
270
+ _backup_current_cont = None
271
+ _before_last_change_cont = None
262
272
  _changed = False
263
273
 
274
+ dump_file: Path = LIB_DUMP_CONFS.joinpath("SSSDConf.json")
275
+
264
276
  def __new__(cls):
265
277
  if cls.__instance is None:
266
278
  cls.__instance = super(SSSDConf, cls).__new__(cls)
@@ -285,24 +297,47 @@ class SSSDConf(File):
285
297
  self._changes = ConfigParser()
286
298
  self._changes.optionxform = str
287
299
 
300
+ if self.dump_file.exists():
301
+ with self.dump_file.open("r") as f:
302
+ cnt = json.load(f)
303
+ self._backup_original = Path(cnt['_backup_original'])
304
+
288
305
  def __call__(self, key: str, value: Union[int, str, bool],
289
306
  section: str = None):
307
+ # We need to save the state of the current unchanged sssd.conf because
308
+ # __call__ is called before __enter__ in
309
+ # with SSSDConf(key, value, section):
310
+ with self._conf_file.open() as config:
311
+ self._before_last_change_cont = config.read()
312
+
290
313
  self.set(key, value, section)
291
314
  self.save()
292
- run("systemctl restart sssd", sleep=5)
315
+ run("systemctl restart sssd", sleep=10)
293
316
  return self
294
317
 
295
318
  def __enter__(self):
319
+ # Check if we changed the file or not and save version before context
320
+ # manager was called
321
+ if self._before_last_change_cont:
322
+ self._backup_current_cont = self._before_last_change_cont
323
+ else:
324
+ with self._conf_file.open() as config:
325
+ self._backup_current_cont = config.read()
296
326
  return self
297
327
 
298
328
  def __exit__(self, exc_type, exc_value, traceback):
299
329
  if self._changed:
300
- copy2(self._backup_default, self._conf_file)
330
+ # Restore sssd.conf to the version before context manager was
331
+ # called
332
+ with self._conf_file.open("w") as config:
333
+ config.write(self._backup_current_cont)
334
+ self._backup_current_cont = None
335
+ self._before_last_change_cont = None
301
336
  self._changed = False
302
337
  if exc_type is not None:
303
338
  logger.error("Exception in virtual smart card context")
304
339
  logger.error(format_exc())
305
- run("systemctl restart sssd", sleep=5)
340
+ run("systemctl restart sssd", sleep=10)
306
341
 
307
342
  def create(self):
308
343
  """
@@ -313,7 +348,7 @@ class SSSDConf(File):
313
348
  with self._conf_file.open() as config:
314
349
  self._default_parser.read_file(config)
315
350
  logger.info(f"{self._conf_file} file exists, loading values")
316
- self._backup_original = self.backup("original")
351
+ self._backup_original = self.backup("sssd-conf-original")
317
352
 
318
353
  except FileNotFoundError:
319
354
  logger.warning(f"{self._conf_file} not present")
@@ -327,10 +362,18 @@ class SSSDConf(File):
327
362
  with self._backup_default.open("w") as bdefault:
328
363
  self._default_parser.write(bdefault)
329
364
 
365
+ with self.dump_file.open("w") as f:
366
+ json.dump({
367
+ "_backup_original": str(self._backup_original)
368
+ }, f)
369
+
330
370
  def set(self, key: str, value: Union[int, str, bool], section: str = None):
331
371
  """
332
372
  Modify or add content of config file represented by ConfigParser object
333
373
 
374
+ If a value is set outside of a context manager, it is the user's
375
+ responsibility to revert it.
376
+
334
377
  :param key: key from section of config file to be updated
335
378
  :type key: str
336
379
  :param value: new value to be stored in [section][key] of config file
@@ -339,7 +382,8 @@ class SSSDConf(File):
339
382
  :type section: str
340
383
  """
341
384
  if len(self._changes.sections()) == 0:
342
- self._changes.read_dict(self._default_parser)
385
+ with self._conf_file.open() as config:
386
+ self._changes.read_file(config)
343
387
 
344
388
  if not self._changes.has_section(section):
345
389
  logger.warning(f"Section {section} not present.")
@@ -386,18 +430,22 @@ class SSSDConf(File):
386
430
  .. note: SSSD service restart is caller's responsibility.
387
431
  """
388
432
 
389
- # FIXME: this implementation would not work in real usage. When setup
390
- # runtime would be finished and in test/cleanup runtime this method
391
- # would be called self._backup_original field would not be set. This
392
- # should be fixed by implementing dump() method that would store all
393
- # required attributes in JSON format load() method that would be used
394
- # in other then setup runtimes to restore (load from JSON) all
395
- # attributes of this object
396
-
397
- if self._backup_original:
398
- copy2(self._backup_original, self._conf_file)
433
+ if self._backup_original and self._backup_original.exists():
434
+ with self._backup_original.open() as original, \
435
+ self._conf_file.open("w") as config:
436
+ config.write(original.read())
437
+ self._backup_original.unlink()
399
438
  else:
400
439
  self.clean()
440
+
441
+ if self._backup_default.exists():
442
+ self._backup_default.unlink()
443
+
444
+ if self.dump_file.exists():
445
+ self.dump_file.unlink()
446
+ logger.debug(f"Removed {self.dump_file} dump file")
447
+
448
+ logger.info("Restored sssd.conf to the original version")
401
449
  self._changed = False
402
450
 
403
451
  def update_default_content(self):
SCAutolib/models/user.py CHANGED
@@ -105,8 +105,11 @@ class User:
105
105
  logger.info(f"Deleting the user {self.username}")
106
106
  run(['userdel', '-f', self.username], check=True)
107
107
  except KeyError:
108
- pass
109
- logger.info(f"User {self.username} is not present on the system")
108
+ logger.info(f"User {self.username} is not present on the system")
109
+
110
+ if self.dump_file.exists():
111
+ self.dump_file.unlink()
112
+ logger.debug(f"Removed {self.dump_file} dump file")
110
113
 
111
114
 
112
115
  class IPAUser(User):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: SCAutolib
3
- Version: 3.0.0
3
+ Version: 3.1.2
4
4
  Summary: Python library for automation tests of smart cards using virtualization.
5
5
  Home-page: https://github.com/redhat-qe-security/SCAutolib
6
6
  Author: Pavel Yadlouski
@@ -1,17 +1,17 @@
1
- SCAutolib/__init__.py,sha256=l_TOwM1Zpcve60Q7NIboekI6OzP3HUN0lK0RF43sCxg,5446
2
- SCAutolib/cli_commands.py,sha256=Po3k5sg-cXCSqKtHIDKwoxBKd4BInWggqODnfncJj8s,5632
3
- SCAutolib/controller.py,sha256=x5YMOHwIBkFxGdzRIHUg89EkPgnLrYwJdatLQVTx5pE,21350
1
+ SCAutolib/__init__.py,sha256=nC78DtNE09z1s-LglB2K_-iquLONQAOfg2qbK_6ay9g,5490
2
+ SCAutolib/cli_commands.py,sha256=5G6JSGskB0igROUufRQ8xpKwoOv6tfzhWDo6o6nI-Ao,5888
3
+ SCAutolib/controller.py,sha256=qRi8k_q5LqCceGDNDDrefcKorqIrUVlvX7UXSM37XVU,22243
4
4
  SCAutolib/enums.py,sha256=3t3N2RRjZeZW2EMhNf1kk5S-FgarabBEo1qLlf6uIzE,638
5
5
  SCAutolib/exceptions.py,sha256=-Jsj80CXOSXQacCI46PYXEIh6-tdHSOw3FE4itE_e5w,857
6
6
  SCAutolib/utils.py,sha256=4BdttTsNzZtletfeoIuIERrdd5BNiJGlcHa1mOFIuSY,8026
7
- SCAutolib/models/CA.py,sha256=wykB5-bCwHdSK5kjrWQ1Qr3kwqG9cOeFoaei9hmU4zU,28557
7
+ SCAutolib/models/CA.py,sha256=Fib5Sa4b8qXN6UVpYr4ar9smsPBTRxMcDiOfGXC3oX0,29583
8
8
  SCAutolib/models/__init__.py,sha256=8NZySkDbAn2sktD1L3__Y37kY9kEXM2o4TnN3hiIsfk,48
9
9
  SCAutolib/models/authselect.py,sha256=PqRcxB9RSAWmGSF1Z8u1YrE7OLrD9oj-sCzGJEAWHa8,3443
10
- SCAutolib/models/card.py,sha256=tL3_ITcaGwCxqrbMzYu1vsCM4zJmYqsIO6O4zp7G2b8,15524
11
- SCAutolib/models/file.py,sha256=VWDxaYf25QV3GCyqhE4DpJGkvPd8I2A5GyNfHuqVepQ,20934
10
+ SCAutolib/models/card.py,sha256=QhGn5hbdZaaEuH9T1jZNNwKTopTApJfQcjP5iSKP5Kk,16149
11
+ SCAutolib/models/file.py,sha256=pr3mNJ82XtcXLTD91B16Bw5Dd4hXlYG9c6fEZYtVdAQ,22685
12
12
  SCAutolib/models/gui.py,sha256=YLzmIHIreolA0gtIYH96LUHDT-d5outEIjAvU-W4UHQ,12253
13
13
  SCAutolib/models/log.py,sha256=6EoiehIIJjCXZqbT_X3eQyKWCS-_yZ5RdJcX5HVTJXI,1499
14
- SCAutolib/models/user.py,sha256=qbiKn6kO2-nJSvItd2wukpv0rMXHndmlrGh88lzases,6266
14
+ SCAutolib/models/user.py,sha256=VK4chlS7izTYiwmVuYjl_cknOe00FFbCbiQOfMOoZU4,6390
15
15
  SCAutolib/templates/ca.cnf,sha256=9oqUZUSy_lEtNLDViD8SwgJl1ZKCI1-DMri1feF6vjQ,1047
16
16
  SCAutolib/templates/gnome_disable_welcome,sha256=POtfU_SrgKGn4RmgLrFtg0K3MTetSFAmUo9HWidi5W0,60
17
17
  SCAutolib/templates/softhsm2.conf,sha256=WAlZpRSLzssZ0-dnUZcz2pig9RGIJD0oQg_t5B1X3Fo,108
@@ -19,9 +19,9 @@ SCAutolib/templates/sssd.conf,sha256=eBQJu9AY7LG4OsHRxinUjUeQOIxSu_MksWPKfqZswYo
19
19
  SCAutolib/templates/user.cnf,sha256=pyyJhxFdOVlFqoVGVwjomOq-W4wEt3YWfRGZEXprwto,452
20
20
  SCAutolib/templates/virt_cacard.service,sha256=31NrSKUspYIKNOVhL4Usc62CImlu3heNZprJ8sdw11Y,299
21
21
  SCAutolib/templates/virtcacard.cil,sha256=TwxknjxnTtDK_KR3-MbKcLM0VrB76JVSlY-j84VaNZY,167
22
- SCAutolib-3.0.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
23
- SCAutolib-3.0.0.dist-info/METADATA,sha256=FziTVWvVhPJe1dCRNXkEPb5LXdaTSW_FIhO336hjOKc,2365
24
- SCAutolib-3.0.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
25
- SCAutolib-3.0.0.dist-info/entry_points.txt,sha256=SyEBTEHEsfYmYZ4L3mQ_RUkW_PRTEWurYgITxGkFLe4,54
26
- SCAutolib-3.0.0.dist-info/top_level.txt,sha256=z2XZ0S23vykXV_dZYNlLcgcSERgBDIWxmNsiiQBL-wQ,10
27
- SCAutolib-3.0.0.dist-info/RECORD,,
22
+ SCAutolib-3.1.2.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
23
+ SCAutolib-3.1.2.dist-info/METADATA,sha256=q_kcpJqXld9i0NMtKKfHLD114CsHl1ChgVo0f8bs2gE,2365
24
+ SCAutolib-3.1.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
25
+ SCAutolib-3.1.2.dist-info/entry_points.txt,sha256=SyEBTEHEsfYmYZ4L3mQ_RUkW_PRTEWurYgITxGkFLe4,54
26
+ SCAutolib-3.1.2.dist-info/top_level.txt,sha256=z2XZ0S23vykXV_dZYNlLcgcSERgBDIWxmNsiiQBL-wQ,10
27
+ SCAutolib-3.1.2.dist-info/RECORD,,