SCAutolib 1.1.0__tar.gz → 3.0.0__tar.gz

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.
Files changed (48) hide show
  1. {SCAutolib-1.1.0/SCAutolib.egg-info → SCAutolib-3.0.0}/PKG-INFO +17 -2
  2. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/__init__.py +20 -21
  3. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/cli_commands.py +2 -1
  4. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/controller.py +168 -106
  5. SCAutolib-3.0.0/SCAutolib/enums.py +40 -0
  6. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/models/CA.py +111 -39
  7. SCAutolib-3.0.0/SCAutolib/models/card.py +445 -0
  8. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/models/file.py +15 -12
  9. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/models/gui.py +2 -1
  10. SCAutolib-3.0.0/SCAutolib/models/user.py +179 -0
  11. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/templates/user.cnf +1 -1
  12. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/utils.py +73 -50
  13. {SCAutolib-1.1.0 → SCAutolib-3.0.0/SCAutolib.egg-info}/PKG-INFO +17 -2
  14. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib.egg-info/SOURCES.txt +1 -0
  15. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/setup.py +1 -1
  16. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/test/test_ca.py +18 -6
  17. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/test/test_card.py +21 -2
  18. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/test/test_cli.py +6 -0
  19. SCAutolib-3.0.0/test/test_controller.py +33 -0
  20. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/test/test_file.py +1 -1
  21. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/test/test_openssl_conf.py +2 -1
  22. SCAutolib-3.0.0/test/test_user.py +26 -0
  23. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/test/test_utils.py +2 -3
  24. SCAutolib-1.1.0/SCAutolib/models/card.py +0 -285
  25. SCAutolib-1.1.0/SCAutolib/models/user.py +0 -346
  26. SCAutolib-1.1.0/test/test_controller.py +0 -59
  27. SCAutolib-1.1.0/test/test_user.py +0 -48
  28. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/LICENSE +0 -0
  29. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/MANIFEST.in +0 -0
  30. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/README.md +0 -0
  31. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/exceptions.py +0 -0
  32. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/models/__init__.py +0 -0
  33. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/models/authselect.py +0 -0
  34. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/models/log.py +0 -0
  35. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/templates/ca.cnf +0 -0
  36. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/templates/gnome_disable_welcome +0 -0
  37. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/templates/softhsm2.conf +0 -0
  38. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/templates/sssd.conf +0 -0
  39. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/templates/virt_cacard.service +0 -0
  40. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib/templates/virtcacard.cil +0 -0
  41. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib.egg-info/dependency_links.txt +0 -0
  42. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib.egg-info/entry_points.txt +0 -0
  43. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib.egg-info/requires.txt +0 -0
  44. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/SCAutolib.egg-info/top_level.txt +0 -0
  45. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/requirements.txt +0 -0
  46. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/setup.cfg +0 -0
  47. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/test/test_sssd_conf.py +0 -0
  48. {SCAutolib-1.1.0 → SCAutolib-3.0.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: SCAutolib
3
- Version: 1.1.0
3
+ Version: 3.0.0
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
@@ -15,8 +15,23 @@ Classifier: Topic :: Software Development :: Testing
15
15
  Classifier: Topic :: Software Development :: Testing :: Acceptance
16
16
  Requires-Python: >=3
17
17
  Description-Content-Type: text/markdown
18
- Provides-Extra: graphical
19
18
  License-File: LICENSE
19
+ Requires-Dist: click>=8
20
+ Requires-Dist: coloredlogs>=15
21
+ Requires-Dist: paramiko>=2.10
22
+ Requires-Dist: fabric>=2.7
23
+ Requires-Dist: invoke>=1.7
24
+ Requires-Dist: pytest>=7
25
+ Requires-Dist: schema>=0.7
26
+ Requires-Dist: python_freeipa>=1.0
27
+ Requires-Dist: pexpect>=4
28
+ Provides-Extra: graphical
29
+ Requires-Dist: python-uinput; extra == "graphical"
30
+ Requires-Dist: opencv-python; extra == "graphical"
31
+ Requires-Dist: pandas; extra == "graphical"
32
+ Requires-Dist: numpy; extra == "graphical"
33
+ Requires-Dist: pytesseract; extra == "graphical"
34
+ Requires-Dist: keyboard; extra == "graphical"
20
35
 
21
36
  # Smart Card Automation library (SCAutolib)
22
37
  Test automation library for Smart Cards.
@@ -1,5 +1,3 @@
1
- from enum import Enum, auto
2
-
3
1
  import coloredlogs
4
2
  import logging
5
3
  import subprocess
@@ -7,6 +5,8 @@ from pathlib import Path
7
5
  import time
8
6
  from schema import Schema, Use, Or, And, Optional
9
7
 
8
+ from SCAutolib.enums import CardType, UserType
9
+
10
10
  fmt = "%(name)s:%(module)s.%(funcName)s.%(lineno)d [%(levelname)s] %(message)s"
11
11
  date_fmt = "%H:%M:%S"
12
12
  coloredlogs.install(level="DEBUG", fmt=fmt, datefmt=date_fmt,
@@ -49,24 +49,23 @@ schema_cas = Schema(And(
49
49
  # Specify validation schema for all users
50
50
  schema_user = Schema({'name': Use(str),
51
51
  'passwd': Use(str),
52
- 'pin': Use(str),
53
- Optional('card_dir', default=None): Use(Path),
54
- 'card_type': Or("virtual", "real", "removinator"),
55
- Optional('cert', default=None): Use(Path),
56
- Optional('key', default=None): Use(Path),
57
- 'local': Use(bool)})
58
-
52
+ 'user_type': Or(UserType.local, UserType.ipa)})
59
53
 
60
- class ReturnCode(Enum):
61
- """
62
- Enum for return codes
63
- """
64
- SUCCESS = 0
65
- MISSING_CA = auto()
66
- FAILURE = auto()
67
- ERROR = auto()
68
- EXCEPTION = auto()
69
- UNKNOWN = auto()
54
+ # Specify validation schema for all cards
55
+ schema_card = Schema({'name': Use(str),
56
+ 'pin': Use(str),
57
+ Optional('card_details', default=None): Use(str),
58
+ 'cardholder': Use(str),
59
+ 'CN': Use(str),
60
+ Optional('UID', default=None): Use(str),
61
+ Optional('expires', default=None): Use(str),
62
+ 'card_type': Or(CardType.virtual, CardType.physical),
63
+ 'ca_name': Use(str),
64
+ Optional('ca_cert', default=None): Use(str),
65
+ Optional('slot', default=None): Use(str),
66
+ Optional('uri', default=None): Use(str),
67
+ Optional('cert', default=None): Use(str),
68
+ Optional('key', default=None): Use(str)})
70
69
 
71
70
 
72
71
  def run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True,
@@ -85,7 +84,7 @@ def run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True,
85
84
  :type sleep: int
86
85
  :param return_code: acceptable return codes from given commands.
87
86
  If check=True, and the return code of the cmd is not in the return_code
88
- list an subprocess.CalledProcessError exception would be raised.
87
+ list a subprocess.CalledProcessError exception would be raised.
89
88
  :type return_code: list
90
89
  :param cmd: Command to be executed
91
90
  :type cmd: list or str
@@ -111,7 +110,7 @@ def run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True,
111
110
  """
112
111
  if return_code is None:
113
112
  return_code = [0]
114
- if type(cmd) == str:
113
+ if isinstance(cmd, str):
115
114
  cmd = cmd.split(" ")
116
115
  logger.debug(f"run: {' '.join([str(i) for i in cmd])}")
117
116
  out = subprocess.run(cmd, stdout=stdout, stderr=stderr, encoding="utf-8",
@@ -6,8 +6,9 @@ import click
6
6
  from pathlib import Path
7
7
  from sys import exit
8
8
 
9
- from SCAutolib import logger, exceptions, schema_user, ReturnCode
9
+ from SCAutolib import logger, exceptions, schema_user
10
10
  from SCAutolib.controller import Controller
11
+ from SCAutolib.enums import ReturnCode
11
12
 
12
13
 
13
14
  @click.group()
@@ -4,15 +4,16 @@ from schema import Schema, Use
4
4
  from shutil import rmtree
5
5
  from typing import Union
6
6
 
7
- from SCAutolib import exceptions, schema_cas, schema_user
7
+ from SCAutolib import exceptions, schema_cas, schema_user, schema_card
8
8
  from SCAutolib import (logger, run, LIB_DIR, LIB_BACKUP, LIB_DUMP,
9
9
  LIB_DUMP_USERS, LIB_DUMP_CAS, LIB_DUMP_CARDS,
10
10
  TEMPLATES_DIR)
11
11
  from SCAutolib.models import CA, file, user, card, authselect as auth
12
- from SCAutolib.models.file import File
13
- from SCAutolib.utils import (OSVersion, _check_selinux, _gen_private_key,
12
+ from SCAutolib.models.file import File, OpensslCnf
13
+ from SCAutolib.enums import (OSVersion, CardType, UserType)
14
+ from SCAutolib.utils import (_check_selinux, _gen_private_key,
14
15
  _get_os_version, _install_packages,
15
- _check_packages, dump_to_json, local_ca_factory)
16
+ _check_packages, dump_to_json, ca_factory)
16
17
 
17
18
 
18
19
  class Controller:
@@ -68,12 +69,31 @@ class Controller:
68
69
  def prepare(self, force: bool, gdm: bool, install_missing: bool,
69
70
  graphical: bool):
70
71
  """
71
- Method for setting up whole system based on configuration file and
72
- CLI commands
72
+ Prepare system for testing. This method provides complex configuration
73
+ of system under test for testing including creation of CAs, users and
74
+ smart cards in the system and objects that represents them in SCAutolib.
75
+ Configuration is based on config file and CLI options.
76
+
77
+ :param force: Defines if existing objects, files, users, services etc.
78
+ should be erased or overwritten if they already exist. True stands
79
+ for erase/overwrite. This parameter is forwarded to several methods
80
+ and it can have slightly different meaning in each of them.
81
+ For details see docstrings of the methods.
82
+ :type force: bool
83
+
84
+ :param gdm: If True, GDM package would be installed
85
+ :type gdm: bool
86
+ :param install_missing: If True, all missing packages would be
87
+ installed
88
+ :type install_missing: bool
89
+ :param graphical: If True, GUI tests dependencies are installed
90
+ :type graphical: bool
73
91
  """
74
92
  self.setup_system(install_missing, gdm, graphical)
75
93
 
76
- # In this method not having section for local CA and/or for IPA CA is OK
94
+ # Prepare CAs: Virtual cards are populated by certificates that are: a)
95
+ # created locally and signed by local CA configured on the system under
96
+ # test, or b) created and signed using FreeIPA.
77
97
  try:
78
98
  self.setup_local_ca(force=force)
79
99
  except exceptions.SCAutolibWrongConfig as e:
@@ -82,9 +102,20 @@ class Controller:
82
102
  self.setup_ipa_client(force=force)
83
103
  except exceptions.SCAutolibWrongConfig as e:
84
104
  logger.info(e)
105
+
85
106
  for usr in self.lib_conf["users"]:
86
- u = self.setup_user(usr, force=force)
87
- self.enroll_card(u)
107
+ self.setup_user(usr, force=force)
108
+
109
+ # Create cards defined in config. For physical cards only objects will
110
+ # be created while for virtual cards tokens will be created and enrolled
111
+ for token in self.lib_conf["cards"]:
112
+ # prepare CA objects for physical cards
113
+ if token["card_type"] == CardType.physical:
114
+ self.setup_custom_ca(token)
115
+ self.setup_card(token)
116
+ elif token["card_type"] == CardType.virtual:
117
+ c = self.setup_card(token)
118
+ self.enroll_card(c)
88
119
 
89
120
  def setup_system(self, install_missing: bool, gdm: bool, graphical: bool):
90
121
  """
@@ -97,6 +128,8 @@ class Controller:
97
128
  :type install_missing: bool
98
129
  :param gdm: If True, GDM package would be installed
99
130
  :type gdm: bool
131
+ :param graphical: If True, GUI tests dependencies are installed
132
+ :type graphical: bool
100
133
  :return:
101
134
  """
102
135
  for d in (LIB_DIR, LIB_BACKUP, LIB_DUMP, LIB_DUMP_USERS, LIB_DUMP_CAS,
@@ -112,13 +145,15 @@ class Controller:
112
145
  packages += ["tesseract", "ffmpeg-free"]
113
146
 
114
147
  # Prepare for virtual cards
115
- if "virtual" in [u["card_type"] for u in self.lib_conf["users"]]:
148
+ if any(c["card_type"] == CardType.virtual
149
+ for c in self.lib_conf["cards"]):
116
150
  packages += ["pcsc-lite-ccid", "pcsc-lite", "virt_cacard",
117
151
  "vpcd", "softhsm"]
118
152
  run("dnf -y copr enable jjelen/vsmartcard")
119
153
 
120
154
  # Add IPA packages if needed
121
- if not all([u["local"] for u in self.lib_conf["users"]]):
155
+ if any([u["user_type"] != UserType.local
156
+ for u in self.lib_conf["users"]]):
122
157
  packages += self._general_steps_for_ipa()
123
158
 
124
159
  # Check for installed packages
@@ -133,7 +168,7 @@ class Controller:
133
168
 
134
169
  if graphical:
135
170
  run(['dnf', 'groupinstall', 'Server with GUI', '-y'])
136
- # disable subsription message
171
+ # disable subscription message
137
172
  run(['systemctl', '--global', 'mask',
138
173
  'org.gnome.SettingsDaemon.Subscription.target'])
139
174
  # disable welcome message
@@ -148,16 +183,16 @@ class Controller:
148
183
  self.sssd_conf.save()
149
184
  self._general_steps_for_virtual_sc()
150
185
 
151
- base_user = user.BaseUser("base-user", "redhat")
186
+ base_user = user.User("base-user", "redhat")
152
187
  base_user.add_user()
153
188
  dump_to_json(base_user)
154
- dump_to_json(user.BaseUser(username="root",
155
- password=self.lib_conf["root_passwd"]))
189
+ dump_to_json(user.User(username="root",
190
+ password=self.lib_conf["root_passwd"]))
156
191
 
157
192
  def setup_local_ca(self, force: bool = False):
158
193
  """
159
194
  Setup local CA based on configuration from the configuration file. All
160
- necessary file for this operation (e.g. CNF file for self-signed root
195
+ necessary files for this operation (e.g. CNF file for self-signed root
161
196
  certificate) would be created along the way.
162
197
 
163
198
  :param force: If local CA already exists in given directory, specifies
@@ -171,11 +206,31 @@ class Controller:
171
206
  raise exceptions.SCAutolibWrongConfig(msg)
172
207
 
173
208
  ca_dir: Path = self.lib_conf["ca"]["local_ca"]["dir"]
174
- self.local_ca = local_ca_factory(ca_dir, force)
209
+ ca_dir.mkdir(exist_ok=True, parents=True)
210
+
211
+ cnf = OpensslCnf(ca_dir.joinpath("ca.cnf"), "CA", str(ca_dir))
212
+ self.local_ca = ca_factory(path=ca_dir, cnf=cnf, create=True)
213
+ if force:
214
+ logger.warning(f"Removing previous local CA from {ca_dir}")
215
+ self.local_ca.cleanup()
216
+ cnf.create()
217
+ cnf.save()
218
+ self.local_ca.setup()
219
+ self.local_ca.update_ca_db()
220
+ run(["systemctl", "restart", "sssd"], sleep=5)
221
+
175
222
  logger.info(f"Local CA is configured in {ca_dir}")
176
223
 
177
224
  dump_to_json(self.local_ca)
178
225
 
226
+ def setup_custom_ca(self, card_data: dict):
227
+ if card_data["card_type"] == CardType.physical:
228
+ ca = ca_factory(create=True, card_data=card_data)
229
+ ca.setup()
230
+ if not ca._ca_cert.is_file():
231
+ raise FileNotFoundError(f"File not found: {ca._ca_cert}")
232
+ dump_to_json(ca)
233
+
179
234
  def setup_ipa_client(self, force: bool = False):
180
235
  """
181
236
  Configure IPA client for given IPA server on current host. IPA server
@@ -210,8 +265,7 @@ class Controller:
210
265
 
211
266
  def setup_user(self, user_dict: dict, force: bool = False):
212
267
  """
213
- Configure the user on the specified system (local machine/CA). The user
214
- would be configured along with the card based on configurations.
268
+ Configure the user on the specified system (local machine/CA).
215
269
 
216
270
  :param force: specify if the user should be re-created with its
217
271
  card directory
@@ -221,41 +275,14 @@ class Controller:
221
275
  :return: the user object
222
276
  """
223
277
  new_user = None
224
- card_dir: Path = user_dict["card_dir"]
225
- # Card dir is home dir of the user home directory
226
- if force and card_dir.exists():
227
- rmtree(card_dir)
228
278
 
229
- if user_dict["local"]:
279
+ if user_dict["user_type"] == UserType.local:
230
280
  new_user = user.User(username=user_dict["name"],
231
- pin=user_dict["pin"],
232
- password=user_dict["passwd"],
233
- card_dir=card_dir,
234
- cert=user_dict["cert"], key=user_dict["key"],
235
- local=True)
281
+ password=user_dict["passwd"])
236
282
  if force:
237
- if new_user.cert and new_user.cert.exists():
238
- self.local_ca.revoke_cert(new_user.cert)
239
283
  new_user.delete_user()
240
- user_dict["card_dir"].mkdir(exist_ok=True)
241
284
  new_user.add_user()
242
285
 
243
- csr_path = new_user.card_dir.joinpath(f"csr-{new_user.username}.csr")
244
- cnf = file.OpensslCnf(filepath=csr_path, conf_type="user",
245
- replace=new_user.username)
246
- cnf.create()
247
- cnf.save()
248
-
249
- new_user.cnf = cnf.path
250
- self.sssd_conf.set(
251
- section=f"certmap/shadowutils/{new_user.username}",
252
- key="matchrule",
253
- value=f"<SUBJECT>.*CN={new_user.username}.*")
254
- self.sssd_conf.save()
255
- self.sssd_conf.update_default_content()
256
- run(["systemctl", "restart", "sssd"])
257
- logger.debug(f"Match rule for user {new_user.username} is added "
258
- f"to /etc/sssd/sssd.conf")
259
286
  else:
260
287
  if self.ipa_ca is None:
261
288
  msg = "Can't proceed in configuration of IPA user because no " \
@@ -263,75 +290,109 @@ class Controller:
263
290
  raise exceptions.SCAutolibException(msg)
264
291
  new_user = user.IPAUser(ipa_server=self.ipa_ca,
265
292
  username=user_dict["name"],
266
- pin=user_dict["pin"],
267
- password=user_dict["passwd"],
268
- card_dir=card_dir,
269
- cert=user_dict["cert"],
270
- key=user_dict["key"],
271
- local=False)
293
+ password=user_dict["passwd"])
272
294
  if force:
273
- if new_user.cert and new_user.cert.exists():
274
- self.ipa_ca.revoke_cert(new_user.cert)
275
295
  new_user.delete_user()
276
- user_dict["card_dir"].mkdir(exist_ok=True)
277
-
278
296
  new_user.add_user()
279
-
280
- if user_dict["card_type"] == "virtual":
281
- hsm_conf = file.SoftHSM2Conf(
282
- new_user.card_dir.joinpath("sofhtsm2.conf"),
283
- new_user.card_dir)
284
- hsm_conf.create()
285
- hsm_conf.save()
286
-
287
- new_user.card = card.VirtualCard(new_user,
288
- softhsm2_conf=hsm_conf.path)
289
- else:
290
- raise NotImplementedError("Other card type than 'virtual' does"
291
- "not supported yet")
292
-
293
- new_user.card.create()
294
297
  self.users.append(new_user)
295
-
296
298
  dump_to_json(new_user)
297
-
298
299
  return new_user
299
300
 
300
- def enroll_card(self, user_: user.User, force: bool = False):
301
+ def setup_card(self, card_dict: dict, force: bool = False):
301
302
  """
302
- Enroll the card of a given user with configured CA. If private key
303
- and/or the certificate do not exists, new one's would be requested
304
- from corresponding CA.
303
+ Create card object. Card object should contain its root CA cert as it
304
+ represents general card (i.e. including physical read-only cards).
305
305
 
306
- :param force:
307
- :param user_: User with a card to be enrolled.
306
+ :param card_dict: Dictionary containing card attributes
307
+ :type card_dict: dict
308
+ :param force: If its true and card directory exists it will be removed
309
+ :type force: bool
308
310
  """
309
- logger.debug(f"Starting enrollment of the card for user "
310
- f"{user_.username}")
311
- if not user_.card:
312
- raise exceptions.SCAutolibException(
313
- f"Card for the user {user_.username} is not initialized")
311
+ card_dir: Path = Path("/root/cards", card_dict["name"])
312
+ card_dir.mkdir(parents=True, exist_ok=True)
314
313
 
315
- if not user_.key.exists():
316
- _gen_private_key(user_.key)
314
+ if force and card_dir.exists():
315
+ rmtree(card_dir)
317
316
 
318
- if not user_.cert.exists():
319
- # Creating a new private key makes sense only if the certificate
320
- # doesn't exist yet
317
+ if card_dict["card_type"] == CardType.physical:
318
+ new_card = card.PhysicalCard(card_dict, card_dir=card_dir)
319
+ elif card_dict["card_type"] == CardType.virtual:
320
+ hsm_conf = self.prepare_softhsm_config(card_dir)
321
+ new_card = card.VirtualCard(card_dict, softhsm2_conf=hsm_conf.path,
322
+ card_dir=card_dir)
323
+ # card needs to know some details of its user
324
+ new_card.user = self.link_user_to_card(new_card)
325
+ if new_card.user.user_type == UserType.local:
326
+ new_card.cnf = self.prepare_user_cnf(new_card)
327
+ if force:
328
+ self.revoke_certs(new_card)
329
+ new_card.create()
330
+ else:
331
+ raise NotImplementedError("Other card types than 'physical' and "
332
+ "'virtual' are not supported")
333
+
334
+ dump_to_json(new_card)
335
+ return new_card
336
+
337
+ def link_user_to_card(self, card: card.VirtualCard):
338
+ for card_user in self.users:
339
+ if card_user.username == card.cardholder:
340
+ return card_user
341
+
342
+ def prepare_softhsm_config(self, card_dir: Path = None):
343
+ """Prepare SoftHSM2 config for virtual card"""
344
+ filepath = card_dir.joinpath("sofhtsm2.conf")
345
+ hsm_conf = file.SoftHSM2Conf(filepath, card_dir=card_dir)
346
+ hsm_conf.create()
347
+ hsm_conf.save()
348
+ return hsm_conf
349
+
350
+ def prepare_user_cnf(self, card: card.VirtualCard):
351
+ """Prepare user openssl cnf"""
352
+ cnf_path = card.card_dir.joinpath(f"{card.cardholder}.cnf")
353
+ cnf = file.OpensslCnf(filepath=cnf_path, conf_type="user",
354
+ replace=[card.cardholder, card.CN])
355
+ cnf.create()
356
+ cnf.save()
357
+ return cnf.path
358
+
359
+ def revoke_certs(self, card: card.VirtualCard):
360
+ if card.cert and card.cert.exists():
361
+ if card.user.user_type == UserType.local:
362
+ self.local_ca.revoke_cert(card.cert)
363
+ else:
364
+ self.ipa_ca.revoke_cert(card.cert)
365
+
366
+ def enroll_card(self, card: card.VirtualCard):
367
+ """
368
+ Enroll the card - i.e. upload keys and certs to card. If private key
369
+ and/or the certificate do not exist, new one's would be requested
370
+ from corresponding CA.
371
+
372
+ :param card: card object
373
+ :type card: card.VirtualCard
374
+ """
375
+ logger.debug(f"Starting enrollment of the card {card.name}")
376
+ if not card:
377
+ raise exceptions.SCAutolibException(
378
+ f"Card {card.name} is not initialized")
321
379
 
322
- csr = user_.gen_csr()
380
+ if not card.key.exists():
381
+ _gen_private_key(card.key)
323
382
 
383
+ if not card.cert.exists():
384
+ csr = card.gen_csr()
324
385
  ca = self.ipa_ca \
325
- if isinstance(user_, user.IPAUser) else self.local_ca
326
- user_.cert = ca.request_cert(csr, user_.username, user_.cert)
386
+ if isinstance(card.user, user.IPAUser) else self.local_ca
387
+ card.cert = ca.request_cert(csr, card.cardholder, card.cert)
327
388
 
328
- user_.card.enroll()
329
-
330
- dump_to_json(user_.card)
331
- # Update the dump for current user to link it with the card
332
- dump_to_json(user_)
389
+ card.enroll()
390
+ dump_to_json(card)
333
391
 
334
392
  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
335
396
  """
336
397
  Clean the system after setup. This method restores the SSSD config file,
337
398
  deletes created users with cards, remove CA's (local and/or IPA Client)
@@ -339,18 +400,18 @@ class Controller:
339
400
  self.sssd_conf.clean()
340
401
 
341
402
  for user_file in LIB_DUMP_USERS.iterdir():
342
- usr = user.BaseUser.load(user_file)
343
- usr.delete()
344
- card_file = LIB_DUMP_CARDS.joinpath(f"card-{usr.username}.json")
403
+ usr = user.User.load(user_file)
404
+ usr.delete_user()
405
+ for card_file in LIB_DUMP_CARDS.iterdir():
345
406
  if card_file.exists():
346
- card.Card.load(card_file, user=user).delete()
407
+ card.Card.load(card_file).delete() # TODO implement card.delete
347
408
 
348
409
  if self.local_ca:
349
- self.local_ca.cleanup()
410
+ self.local_ca.cleanup() # FIXME restore sssd_auth_ca_db.pem file
350
411
  if self.ipa_ca:
351
412
  self.ipa_ca.cleanup()
352
413
 
353
- self.authselect.cleanup()
414
+ self.authselect.cleanup() # TODO implement authselect.cleanup
354
415
 
355
416
  @staticmethod
356
417
  def _validate_configuration(conf: dict, params: {} = None) -> dict:
@@ -376,7 +437,8 @@ class Controller:
376
437
  # Specify general schema for whole config file
377
438
  schema = Schema({"root_passwd": Use(str),
378
439
  "ca": schema_cas,
379
- "users": [schema_user]})
440
+ "users": [schema_user],
441
+ "cards": [schema_card]})
380
442
 
381
443
  return schema.validate(conf)
382
444
 
@@ -0,0 +1,40 @@
1
+ from enum import Enum, auto
2
+
3
+
4
+ class OSVersion(Enum):
5
+ """
6
+ Enumeration for Linux versions. Used for more convenient checks.
7
+ """
8
+ Fedora = 1
9
+ RHEL_9 = 2
10
+ RHEL_8 = 3
11
+ CentOS_8 = 4
12
+ CentOS_9 = 5
13
+
14
+
15
+ class CardType(str, Enum):
16
+ physical = "physical"
17
+ virtual = "virtual"
18
+
19
+
20
+ class UserType(str, Enum):
21
+ local = "local"
22
+ ipa = "ipa"
23
+
24
+
25
+ class CAType(str, Enum):
26
+ local = "local"
27
+ custom = "custom"
28
+ ipa = "IPA"
29
+
30
+
31
+ class ReturnCode(Enum):
32
+ """
33
+ Enum for return codes
34
+ """
35
+ SUCCESS = 0
36
+ MISSING_CA = auto()
37
+ FAILURE = auto()
38
+ ERROR = auto()
39
+ EXCEPTION = auto()
40
+ UNKNOWN = auto()